<?php
/**
 * Created by PhpStorm.
 * User: krystal
 * Date: 2019/4/18
 * Time: 13:31
 */

namespace App\Models\CacheModel;

use App\Models\CacheModel\Goods as GoodsCacheModel;
use App\Models\CacheModel\live\LiveAnchorInfo;
use App\Models\CacheModel\live\LiveAnswerAstroRecord;
use App\Models\CacheModel\live\LiveAnswerDiceRecord;
use App\Models\CacheModel\live\LiveAnswerTarotRecord;
use App\Models\CacheModel\live\LiveGoodsActivity;
use App\Models\CacheModel\live\LiveRecordingInfo;
use App\Models\CacheModel\name_analyze\Zl9NameAnalyzeCreateNameRecord;
use App\Models\CacheModel\name_analyze\Zl9NameAnalyzeRecord;
use App\Models\CacheModel\Orders as OrdersCacheModel;
use App\Models\CacheModel\OrdersLib as OrdersCacheModelLib;
use App\Models\CacheModel\privatearea\PrivateAreaMallGoodsAttr;
use App\Models\CacheModel\privatearea\PrivateAreaOrders;
use App\Models\CacheModel\privatearea\PrivateAreaQuestionInfo;
use App\Models\CacheModel\zl1_relation\Zl1RelationAnalysisFitYou;
use App\Models\CacheModel\zl1_relation\Zl1RelationAnalysisIdeaSubmitCase;
use App\Models\CacheModel\zl1_relation\Zl1RelationAnalysisReport;
use App\Models\CacheModel\zl3\Zl3FortuneYearAnalysis;
use App\Models\CacheModel\zl4\Zl4GoodTimeTag;
use App\Models\CacheModel\zl4\Zl4HoroscopeGoodTimeTemp;
use App\Models\CacheModel\zl4\Zl4HoroscopeReport;
use App\Models\CacheModel\zl6\Zl6Meditation;
use App\Models\CacheModel\zl6\Zl6MeditationUnlockData;
use App\Models\CacheModel\zlcr2\Zlcr2UserAddQuestion;
use App\Models\CacheModel\zlcr2\Zlcr2UserQuestion;
use app\common\lib\Aes;
use app\common\lib\exception\ApiException;
use app\common\lib\Util;
use App\Models\Api\MessageCenter;
use app\common\pay\Ali;
use app\common\pay\Weixin;
use Exception;
use think\Cache;
use think\Db;
use think\Env;
use think\Log;
use think\response\Json;
use AstroTool\Cache\CacheManage;

/**
 * 订单拓展类库
 * Class Orders
 * @package app\common\lib
 */
class OrdersLib
{

    public static $arr = [
        'goods_data' => [],
        'recode_data' => [],
        'rectification_data' => [],
        'fortune_data' => [
            'start_time' => '',
            'end_time' => '',
        ],
        'video_data' => [
            'title' => '',
        ],
    ];

    protected static $recodeInfo1 = null;
    protected static $recodeInfo2 = null;


    /**
     *  * 统一三方下单入口
     * @param array $map
     * @param int $goods_type //1=>虚拟,2=>实体
     * @return array|bool
     * @throws Exception
     */
    public static function unifyPayOrder($map = [], $goods_type = 1)
    {
        $trade_type = (int)$map['trade_type'];
        $pay_des = $map['pay_des'];
        $total_amount = $map['total_amount'];
        $order_id = $map['order_id'];
        $oid = isset($map['order_id']) ? $map['oid'] : 0;
        $openid = $map['openid'];
        $time = time();
        $callback_url = $map['callback_url'];
        $order_type = (int)$map['order_type'];
        $arr = [
            'status' => false,
            'data' => [],
        ];
        //        支付订单
        switch (true)
        {
            // 支付宝APP支付 支付类型为 APP
            case $trade_type === 1:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new Ali();
                $pay_url = $AliPay->pay($pay_des, $order_id, $total_amount, 'APP', '', $goods_type);
                $arr = [
                    'status' => true,
                    'data' => [
                        'pay_url' => $pay_url,
                        'order_id' => $order_id,
                        'oid' => $oid,
                    ],
                ];
                break;
            // 微信支付 2=>APP支付  6=>JSAPI支付 8=>H5 13=>小程序支付
            case in_array($trade_type, [2, 6, 8, 13, 15], true):
//                $total_amount = sprintf('%u', $total_amount * 100);// 以分为单位的
                // 修复小数点精度丢失问题
                $total_amount = sprintf('%u', bcmul($total_amount, 100));// 以分为单位的
                $WxPay = new Weixin();
                // 获取支付类型 APP、JSAPI 或 H5
                $accountConfig = self::getOrderAccountType($order_id, 2);
                $wxpayConfig = \app\common\lib\Orders::getPayConfig('wx', $accountConfig['account']);

                if (empty($wxpayConfig))
                {
                    return $arr;
                }

                $wxPayType = $wxpayConfig['wx_pay_type'][$trade_type];
                // 获取微信配置信息
                $wxConfig = $wxpayConfig[$wxPayType['configName']];

                $data = $WxPay->pay($pay_des, $order_id, $total_amount, $wxPayType['type'], $wxConfig, $openid, $goods_type);
                // 下单成功
                if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS')
                {
                    switch ($trade_type)
                    {
                        // APP支付
                        case 2:
                            $sign['appid'] = $wxConfig['appid'];
                            $sign['partnerid'] = $wxConfig['mchid'];
                            $sign['prepayid'] = $data['prepay_id'];
                            $sign['package'] = 'Sign=WXPay';
                            $sign['noncestr'] = $WxPay->getRandChar(32);
                            $sign['timestamp'] = $time;
                            $sign['sign'] = $WxPay->getSign($sign, $wxConfig);
                            $sign['order_id'] = $order_id;
                            $sign['oid'] = $oid;

                            $data['nonce_str'] = $sign['noncestr'];
                            $data['sign'] = $sign['sign'];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // JSAPI支付
                        case 13:
                        case 15:
                        case 6:
                            $sign = [
                                'appId' => $wxConfig['appid'],
                                'timeStamp' => $time,
                                'nonceStr' => $WxPay->getRandChar(32),
                                'package' => 'prepay_id=' . $data['prepay_id'],
                                'signType' => 'MD5',
                            ];
                            $sign['paySign'] = $WxPay->getSignOfJsApi($sign, $wxConfig);

                            $sign['order_id'] = $order_id;
                            $sign['oid'] = $oid;
                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // H5支付返回
                        case 8:
                            $sign = [
                                'mweb_url' => $data['mweb_url'] . '&redirect_url=' . ($order_type === 1 ? $callback_url : urlencode($callback_url) . $order_id),
                                'order_id' => $order_id,
                                'oid' => $oid,
                            ];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        default:
                            break;
                    }
                } else
                {
                    // $ret = [
                    //     'return_code' => $data['return_code'],
                    //     'return_msg' => $data['return_msg'],
                    // ];
                    $arr = [
                        'status' => false,
                        'data' => $data,
                    ];
                }
                break;
            // 京东支付
            case $trade_type === 5:
//                $total_amount = sprintf("%u", $total_amount * 100);// 以分为单位的
//                $JdPay = new Jd();
//                $data = $JdPay->pay($pay_des, $order_id, $total_amount,$goods_type);
//                if ($data === false)
//                {
//                    $arr = [
//                        'status' => false,
//                        'data' => $data
//                    ];
//                } else
//                {
//                    $arr = [
//                        'status' => true,
//                        'data' => $data
//                    ];
//                }


                $data = [
                    'order_id' => $order_id,
                    'oid' => $oid,
                    'return_code' => 0,
                    'return_msg' => '京东支付支付错误，非常抱歉！',
                ];
                $arr = [
                    'status' => false,
                    'data' => $data,
                ];

                break;
            // 支付宝手机网页支付 支付类型为 WAP
            case $trade_type === 7:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new Ali();
//                $payForm = $AliPay->pay($pay_des, $order_id, $total_amount, 'WAP', $order_type === 1 ? $callback_url : urlencode(urldecode($callback_url) . $order_id), $goods_type);
                //注意：一定不要urlencode，否则 用户点击完成，跳转地址为“https://mclient.alipay.com/h5/”+$callback_url（这尼玛上哪说理去）
                $payForm = $AliPay->pay($pay_des, $order_id, $total_amount, 'WAP', $callback_url, $goods_type);

                $arr = [
                    'status' => true,
                    'data' => [
                        'par_form' => $payForm,
                        'order_id' => $order_id,
                        'oid' => $oid,
                    ],
                ];
                break;
            case $trade_type === 9:
                $arr = [
                    'status' => true,
                    'data' => [
                        'order_id' => $order_id,
                        'oid' => $oid,
                    ],
                ];
                break;
            case in_array($trade_type, [10, 11], true)://字节跳动小程序支付 10=>支付宝APP 11=>微信H5
                //商户配置
                $config_tt = config('miniapp')['TT'];

                $orderInfo = [
                    'merchant_id' => $config_tt['merchant']['merchant_id'],
                    'app_id' => $config_tt['merchant']['app_id'],
                    'sign_type' => 'MD5',
                    'timestamp' => time(),
                    'version' => '2.0',
                    'trade_type' => 'H5',
                    'product_code' => 'pay',
                    'payment_type' => 'direct',
                    'out_order_no' => $oid,
                    'total_amount' => (int)(sprintf('%u', bcmul($total_amount, 100))),//金额，整型，单位：分（不能有小数）
                    'currency' => 'CNY',
                    'trade_time' => time(),
                    'valid_time' => '300',
                    'notify_url' => 'https://www.goddessxzns.com/',
                    'body' => $map['pay_des'],
                    'subject' => $map['pay_des'],//商户订单名称；如果是测试接入，请务必填写：xxx 业务xx 活动测试，例如，懂车帝xx 活动测试
                    'uid' => $map['uid'],
                ];
                if ($trade_type === 10)
                {
                    //支付宝APP支付
                    $AliPay = new Ali();
                    $pay_url = $AliPay->pay($pay_des, $order_id, $total_amount, 'APP', '', $goods_type);
                    $orderInfo['alipay_url'] = $pay_url;
                } else
                {
                    // 微信h5支付
                    $total_amount = sprintf('%u', bcmul($total_amount, 100));// 以分为单位的
                    $WxPay = new Weixin();
                    // 获取支付类型 APP、JSAPI 或 H5
                    $accountConfig = self::getOrderAccountType($order_id, 2);
                    $wxpayConfig = \app\common\lib\Orders::getPayConfig('wx', $accountConfig['account']);

                    if (empty($wxpayConfig))
                    {
                        return $arr;
                    }
                    $wxPayType = $wxpayConfig['wx_pay_type'][8];
                    // 获取微信配置信息
                    $wxConfig = $wxpayConfig[$wxPayType['configName']];

                    $data = $WxPay->pay($pay_des, $order_id, $total_amount, $wxPayType['type'], $wxConfig, $openid, $goods_type);
                    // 下单成功
                    if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS')
                    {
                        $orderInfo['wx_url'] = $data['mweb_url'];
                        $orderInfo['wx_type'] = 'MWEB';
                    } else
                    {
                        $ret = [
                            'return_code' => $data['return_code'],
                            'return_msg' => $data['return_msg'],
                        ];

                        return ['status' => false, 'data' => $ret,];
                    }

                }
                ksort($orderInfo);
                $sign = MD5(self::formatBizQueryParaMap($orderInfo, false) . $config_tt['merchant']['app_secret']);
                $orderInfo['sign'] = $sign;
                $orderInfo['risk_info'] = json_encode(['order_id' => $oid]);

                $arr = [
                    'status' => true,
                    'data' => [
                        'order_info' => $orderInfo,
                        'order_id' => $order_id,
                        'oid' => $oid,
                    ],
                ];

                break;
            default :
                return false;
        }

        return $arr;
    }

    /**
     * 将数组转成uri字符串
     * @param $paraMap
     * @param $urlencode
     * @return false|string
     */
    protected static function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        $reqPar = '';
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if ($urlencode)
            {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, -1);
        }
        return $reqPar;
    }

    /**
     * 检验订单支付状态
     * @param int $uid
     * @param array $gids
     * @param int $order_type
     * @param int $rid
     * @param int $start_time
     * @param int $reid
     * @param int $rid2
     * @return array
     * @throws Exception
     */
    public static function CheckOrder($uid, $gids, $order_type, $rid = 0, $start_time = 0, $reid = 0, $rid2 = 0, $dataId = 0, $signId = 0)
    {
        $res = [
            'msg' => '',
        ];
        //校准生辰检测reid是否存在未支付，计算中和已完成的订单
        //运势检测购买的时间范围
        //地图，视频，本命预测检测购买的商品
        //校准生辰检测reid
        //vip无需检测
        $gid = $gids ? (int)current($gids) : 0;
        $order_type = (int)$order_type;
        $rec = Util::CheckAppVersion('2.1.0');
        switch (true)
        {
            case $order_type === 1:
                $res = self::CheckRecOrder($reid);
                break;
            case $rec === '小' && in_array($order_type, [2, 3, 4, 12], true)://旧版购买运势需要检测是否购买
                $res = self::CheckFortuneOrder($uid, $rid, $order_type, $gids, $start_time);
                break;
            case $order_type === 5:
            case $order_type === 6:
            case $order_type === 7:
            case $order_type === 8:
            case $order_type === 13:
                $res = self::CheckGidsOrder($uid, $rid, $order_type, $gids, $rid2);
                break;
            case $order_type === 27: //年运拓展
                $recode_info = (new Recode())->getInfoById($rid);
                $type = $gid === 91 ? 2 : 3;
                if ($type === 2)
                {
                    $year = date('Y', $start_time);
                    $stageyearData = Util::getRecodeStageyear($recode_info, $year);
                    if ($stageyearData['num'] === 0)
                    {
                        return [
                            'msg' => '当前解锁人生阶段异常',
                        ];
                    }
                } else
                {
                    $stageyearData = ['num' => 0];
                    $year = 0;
                }
                $yearFortuneAnalysisReport = (new Zl3FortuneYearAnalysis())->getFortuneAnalysis($recode_info, [$type], $year, $stageyearData);
                if ($yearFortuneAnalysisReport && $yearFortuneAnalysisReport[$type] && $yearFortuneAnalysisReport[$type]['recode_is_change'] === false)
                {
                    return [
                        'msg' => '当前商品已购买',
                    ];
                }
                break;
            case $order_type === 28:
                $recode_info = (new Recode())->getInfoById($rid);
                if (Goods::isBaziJsjr($order_type, $gid) === false)
                {
                    if ($gid === 106)
                    {
                        $type = 2;
                    } else
                    {
                        $type = 1;
                    }
                    $result = (new Zl4HoroscopeReport())->getReport($recode_info, [$type]);
                    if ($result && $result[$type] && $result[$type]['recode_is_change'] === false)
                    {
                        return [
                            'msg' => '当前商品已购买',
                        ];
                    }
                }
                break;
            case $order_type === 29:
                $res = (new Zl6MeditationUnlockData())->where(['uid' => $uid, 'mid' => $dataId, 'sign' => $signId])->count('id');
                if ($res)
                {
                    throw new ApiException('当前冥想音频已购买');
                }
                break;
        }
        return $res;
    }

    /**
     * 检测校准生辰订单信息
     * @param $reid
     * @return array
     */
    private static function CheckRecOrder($reid)
    {
        try
        {
            $rec = (new OrdersCacheModel())->where(['reid' => $reid, 'status' => ['neq', 2]])->column('id,uid', 'id');
        }
        catch (Exception $e)
        {
            $rec = [];
        }
        $res = [
            'msg' => '',
        ];
        if ($rec)
        {
            $res['msg'] = '已下单，请返回订单列表查看';
        }
        return $res;
    }

    /**
     * 检测运势订单信息
     * @param int $uid
     * @param int $rid
     * @param int $order_type
     * @param array $gids
     * @param int $start_time
     * @return array
     * @throws Exception
     */
    private static function CheckFortuneOrder($uid, $rid, $order_type, $gids, $start_time)
    {
        if ($order_type == 12)
        {
            $fortune_type = 3;
        } else
        {
            $fortune_type = $order_type;
        }
        $goods = (new Goods())->batchGetInfoByIds($gids);
        $goods = $goods ? current($goods) : [];
        $end_time = strtotime($goods['expire_time'], $start_time) - 86400;
        //获取当前购买的时间范围
        switch ($fortune_type)
        {
            case 2:
                $time1 = date('Y-n-j', $start_time);
                $time2 = date('Y-n-j', $end_time);
                break;
            case 3:
            case 12:
                $time1 = date('Y-n', $start_time);
                $time2 = date('Y-n', $end_time);
                break;
            case 4:
                $time1 = date('Y', $start_time);
                $time2 = date('Y', $end_time);
                break;
            default :
                $time1 = '';
                $time2 = '';
        }
        $unlock_times = prDates(['start' => $time1, 'end' => $time2, 'type' => $fortune_type - 1]);
        $unlock_times = array_map(function ($timeValue)
        {
            if (strlen($timeValue) === 4)
            {
                $timeValue .= '-01-01';
            }
            return strtotime($timeValue);
        }, $unlock_times);
        //检测已支付和已赠送运势
        $res = self::CheckPayFortune($uid, $rid, $order_type, $unlock_times);
        if ($res['msg'])
        {
            return [
                'msg' => $res['msg'],
            ];
        }
        return [
            'msg' => '',
        ];
    }

    /**
     * 检测已支付和已赠送运势
     * @param int $uid
     * @param int $rid
     * @param int $order_type
     * @param array $unlock_times
     * @return array
     * @throws Exception
     */
    public static function CheckPayFortune($uid, $rid, $order_type, $unlock_times)
    {
        $res = [];
        $unlock_data = FortuneCache::getUnlockFortune($uid, $rid, $order_type);
        if ($unlock_data['unlock_fortune'])
        {
            $res = array_intersect($unlock_data['unlock_fortune'], $unlock_times);
        }
        if (count($res) >= 1)
        {
            return [
                'msg' => '您购买的运势时间已解锁，请重新确认。',
            ];
        }
    }

    /**
     * 检测商品购买信息
     * @param $uid
     * @param $rid
     * @param $order_type
     * @param $gids
     * @return array
     * @throws Exception
     */
    private static function CheckGidsOrder($uid, $rid, $order_type, $gids, $rid2)
    {
        //若不是购买vip，则判断是否已经购买过该商品
        if ((int)$order_type !== 8)
        {
            $res = self::CheckPayGids($uid, $rid, $order_type, $gids, $rid2);

            if ($res['msg'])
            {
                return $res;
            }
        }
        self::CheckUnpaidGids($uid, $rid, $order_type, $gids);
        return [
            'msg' => '',
        ];
    }

    /**
     * 检测已购买和赠送商品
     * @param $uid
     * @param $rid
     * @param $order_type
     * @param $gids
     * @return array
     * @throws Exception
     */
    public static function CheckPayGids($uid, $rid, $order_type, $gids, $rid2)
    {
        if ($order_type === 7 && Util::CheckAppVersion('2.6.0') !== '小')
        {
            $res = (new NatalReport())->getUnlockGids($uid, $rid);
            if (!array_intersect($gids, $res['gids']))
            {
                $res['msg'] = '';
            }
            return $res;
        }

        if ($order_type === 23)
        {
            $res = (new Zl1RelationAnalysisFitYou())->getUnlockGids($uid, $rid);
            if (!array_intersect($gids, $res['gids']))
            {
                $res['msg'] = '';
            }
            return $res;
        }

        if (in_array($order_type, [25, 26], false))
        {
            $type = $order_type == 25 ? 1 : 2;
            $res = (new Zl1RelationAnalysisReport())->getUnlockGids((int)$uid, $type, $rid, $rid2);
            if (!array_intersect($gids, $res['gids']))
            {
                $res['msg'] = '';
            }
            return $res;
        }

        $rid = (int)$rid;
        $OrdersCacheModel = new OrdersCacheModel();
        $prepaids = $OrdersCacheModel->PrepaidUnpaidOrder($uid, $rid, $order_type, 1);
        $type = 0;
        $key_name = '';
        switch ($order_type)
        {
            case  5:
                $type = 1;
                $key_name = 'acgmap_data';
                break;
            case  6:
                $type = 2;
                $key_name = 'video_data';
                break;
            case  7:
            case  13:
                $key_name = 'data';
                break;
        }
        $unlock_gids = [];
        foreach ($prepaids as $prepaid)
        {
            $tmpGids = isset($prepaid[$key_name]) ? array_column($prepaid[$key_name], 'gid') : [];
            $unlock_gids = $unlock_gids ? array_merge($unlock_gids, $tmpGids) : $tmpGids;
        }
        $resTmp = self::resetPayGids($unlock_gids, $gids);
        $gname = $resTmp['gname'];
        $unlock_gids = $resTmp['unlock_gids'];


        //判断当前档案是否是自己的档案
        $recode = (new Recode())->getRecodeLists($uid, 1);
        if ($recode && (int)$recode['id'] === $rid)
        {
            //获取已赠送的商品名称
            $awards = (new AwardGoods())->getUserAwardFromCache($uid, $type);
            $unlock_gids2 = array_column($awards, 'gid');

            $resTmp = self::resetPayGids($unlock_gids2, $gids);
            $gname = array_merge($gname, $resTmp['gname']);
            $unlock_gids = array_merge($unlock_gids, $resTmp['unlock_gids']);
        }
        $arr = [
            'msg' => $gname ? implode(',', $gname) . '已购买' : '',
            'gids' => $unlock_gids,
        ];
        return $arr;
    }

    /**
     * 重组已解锁gid
     * @param array $unlockGids 已购买的gid
     * @param array $gids 下单的gid
     * @return array
     * @throws Exception
     */
    protected static function resetPayGids(array $unlockGids, array $gids)
    {
        $goods = (new Goods())->batchGetInfoByIds($gids);
        $gname = [];
        $goodsGidConfig = config('goods');
        //幸运之城打包解锁2
        if (in_array(105, $unlockGids, false))
        {
            $gname[] = '当前全部商品';
            $unlockGids = $goodsGidConfig['acg_id'];
        } //幸运之城打包解锁1
        elseif (in_array(19, $unlockGids, false))
        {
            if (Util::CheckAppVersion('2.9.6') === '小')
            {
                $gname[] = '当前全部商品';
            }
            $unlockGids = array_merge($unlockGids, $goodsGidConfig['acg_line_id'], $goodsGidConfig['acg_city_id'], [18, 103, 104]);
        } elseif (in_array(87, $unlockGids, false))
        {
            $gname[] = '最适合你的Ta打包解锁';
            $unlockGids = array_merge($unlockGids, [84, 85, 86, 87]);
        } else
        {
            $tmpGids = array_intersect($unlockGids, $gids);
            $unlockGids = [];
            foreach ($tmpGids as $tmpGid)
            {
                $gname[] = $goods[$tmpGid]['name'];
                $unlockGids[] = $tmpGid;
            }
        }
        return [
            'gname' => $gname,
            'unlock_gids' => $unlockGids, //下单gid中已购买的
        ];
    }

    /**
     * 检测未支付商品
     * @param $uid
     * @param $rid
     * @param $order_type
     * @param $gids
     * @return bool
     * @throws Exception
     */
    public static function CheckUnpaidGids($uid, $rid, $order_type, $gids)
    {
        $OrdersCacheModel = new OrdersCacheModel();
        $prepaids = $OrdersCacheModel->PrepaidUnpaidOrder($uid, $rid, $order_type, 0);
        switch ($order_type)
        {
            case  5:
                $key_name = 'acgmap_data';
                break;
            case  6:
                $key_name = 'video_data';
                break;
            case  7:
                $key_name = 'data';
                break;
            default:
                $key_name = '';
                break;
        }
        foreach ($prepaids as $prepaid)
        {
            if (isset($prepaid[$key_name]))
            {
                foreach ($prepaid[$key_name] as $item)
                {
                    if (in_array($item['gid'], $gids))
                    {
                        $OrdersCacheModel->DelData($prepaid['order_id'], ['is_del' => 1]);
                        //若订单使用了优惠券,退回优惠券
                        $extend = $prepaid['extend'] ? json_decode($prepaid['extend'], true) : [];
                        $order_coupon = $extend['coupon'] ?? '';//获取订单的优惠券id
                        if ($order_coupon)
                        {
                            self::CloseOrder($prepaid);
                        }
                    }
                }
            } else
            {
                if (in_array($prepaid['gid'], $gids))
                {
                    $OrdersCacheModel->DelData($prepaid['order_id'], ['is_del' => 1]);
                    //若订单使用了优惠券,退回优惠券
                    $extend = $prepaid['extend'] ? json_decode($prepaid['extend'], true) : [];
                    $order_coupon = $extend['coupon'] ?? '';//获取订单的优惠券id
                    if ($order_coupon)
                    {
                        self::CloseOrder($prepaid);
                    }
                }
            }
        }
        return true;
    }

    /**
     * 获取订单详情
     * @param $orders
     * @return array | bool
     * @throws Exception
     */
    public static function getOrderDetails($orders)
    {
        //获取订单不同状态下的按钮
        $order_type = (int)$orders['order_type'];
        $btns = config('zhunleorder');
        $extend = $orders['extend'] ? json_decode($orders['extend'], true) : [];
        $create_time = $orders['create_time'];
        $valid_time = $create_time + $btns['close_order_time'];//30分钟未支付关闭订单
        $time_remaining = $valid_time - time();
        if ((int)$orders['status'] === 0 && $time_remaining <= 0)
        {
            $time_remaining = 0;
            $orders['status'] = 2;
        }
        $order_coupon = $extend['coupon'] ?? '';//获取订单的优惠券id
        $order_active = $extend['active'] ?? '';//获取订单的活动id
        $data = $data['order_data'] = [];
        switch ($orders['trade_type'])
        {
            case 1:
            case 7:
                $trade_type = '支付宝支付';
                break;
            case 2:
            case 6:
            case 8:
            case 13:
            case 15:
                $trade_type = '微信支付';
                break;
            case 4:
                $trade_type = '准币支付';
                break;
            case 9:
                $trade_type = '苹果支付';
                break;
            default:
                $trade_type = '未知的支付';
                break;
        }
        $data['order_data'][] = [
            'name' => '下单时间',
            'value' => date('Y-m-d H:i:s', $orders['create_time']),
        ];
        // 苹果支付不显示支付方式
        if ((int)$orders['trade_type'] !== 9)
        {
            $data['order_data'][] = [
                'name' => '支付方式',
                'value' => $trade_type,
            ];
        }
        if ($orders['pay_time'])
        {
            $data['order_data'][] = [
                'name' => '支付时间',
                'value' => date('Y-m-d H:i:s', $orders['pay_time']),
            ];
        }
        $data['status'] = $orders['status'];
        $data['total_amount'] = $orders['total_amount'];
        $data['is_skip'] = 1;
        $data['is_display_recommend_data'] = 0;
        //下单环境 1=>app,2=>微信小程序,3=>抖音小程序,4=>支付宝小程序,5=>百度小程序,6=>QQ小程序,7=>H5
        switch ($orders['env_type'])
        {
            case 2:
                $data['mini_type_des'] = '（微信）';
                break;
            case 3:
                $data['mini_type_des'] = '（抖音）';
                break;
            case 4:
                $data['mini_type_des'] = '（支付宝）';
                break;
            case 5:
                $data['mini_type_des'] = '（百度）';
                break;
            case 6:
                $data['mini_type_des'] = '（QQ）';
                break;
            case 7:
                $data['mini_type_des'] = '（h5）';
                break;
            default:
                $data['mini_type_des'] = null;
                break;
        }
        $data['mini_type'] = $orders['env_type'];
        //订单关闭，等待支付，等待计算（校准生辰）不可跳转
        switch ($orders['status'])
        {
            case 0:
                $data['status_des'] = '等待支付';
                $data['des'] = '请在' . ($btns['close_order_time'] / 60) . '分钟内完成支付';
                $data['time_remaining'] = $time_remaining;
                $data['is_skip'] = 0;
                break;
            case 1:
                //若是有队列的商品，则增加订单描述
                if (in_array($order_type, [1, 3, 4, 5, 7], true))
                {
                    $data['des'] = '购买成功后，24小时内会给您预测结果';
                    $data['status_des'] = '预测进行中';
                } else
                {
                    $data['status_des'] = '订单完成';
                }
                if ($order_type === 1)
                {
                    $data['is_skip'] = 0;
                }
                break;
            case 2:
                $data['status_des'] = '订单关闭';
                $data['is_display_recommend_data'] = 1;
                $data['is_skip'] = 0;
                break;
            case 3:
                $data['status_des'] = '退款成功';
                $data['is_skip'] = 0;
                break;
            case 4:
                $data['status_des'] = '订单已完成';
                $data['is_display_recommend_data'] = 1;
                break;
        }
        //获取商品信息和档案信息，校准生辰信息
        switch (true)
        {
            case $order_type === 1:
                $info = self::RecRead($orders);
                //若校准生辰矫正失败，则不能跳转
                if ($info['rectification_data']['status'] == 3)
                {
                    $data['is_skip'] = 0;
                }
                break;
            case in_array($order_type, [2, 3, 4], true):
                $info = self::FortuneRead($orders);
                break;
            case in_array($order_type, [5, 7], true):
                $info = self::MapRead($orders);
                break;
            case $order_type === 6:
                $info = self::VideoRead($orders);
                break;
            case $order_type === 8:
                $info = self::VipRead($orders);
                break;
            default:
                return false;
        }
        if ($order_coupon)
        {
            $data['coupon_data'] = [
                'name' => $order_coupon['name'],
                'title' => $order_coupon['title'],
                'discount_price' => sprintf('%.2f', $order_coupon['discount_price']),
            ];
        }
        if ($order_active)
        {
            $data['active_data'] = [
                'name' => $order_active['name'],
                'discount_price' => sprintf('%.2f', $order_active['discount_price']),
            ];
        }
        return array_merge($info, $data);
    }

    /**
     * 校准生辰详情
     * @param $data
     * @return array
     * @throws Exception
     */
    public static function RecRead($data)
    {
        //获取商品信息
        self::$arr['goods_data'] = (new Goods())->batchGetInfoByIds($data['gid']);
        //获取校准生辰信息
        self::$arr['rectification_data'] = $rectifition = (new Rectification())->getInfoById($data['reid']);
        //获取档案信息
        self::$arr['recode_data'] = (new Recode())->batchGetInfoByIds($rectifition['rid']);
        return self::$arr;
    }

    /**
     * 运势详情
     * @param array $data
     * @return array|Json
     * @throws Exception
     */
    public static function FortuneRead($data)
    {
        //获取商品信息
        self::$arr['goods_data'] = (new Goods())->batchGetInfoByIds($data['gid']);
        $goods = self::$arr['goods_data'] ? current(self::$arr['goods_data']) : [];
        $fortune = current((new Fortune())->batchGetInfo([$data['fid']]));
        $extend = json_decode($data['extend'], true);
        //判断该笔订单是否是活动期间购买的
        if (isset($extend['expire_time']) && $extend['expire_time'])
        {
            $goods['expire_time'] = $extend['expire_time'];
        }
        //获取档案信息
        self::$arr['recode_data'] = (new Recode())->batchGetInfoByIds($fortune['rids']);
        self::$arr['fortune_data']['fortune_type'] = $fortune['type'];
        self::$arr['fortune_data']['rids'] = $fortune['rids'];
        switch ($data['order_type'])
        {
            case 2:
                self::$arr['fortune_data']['start_time'] = date('Y-m-d', $fortune['time']);
                self::$arr['fortune_data']['end_time'] = date('Y-m-d', strtotime($goods['expire_time'], $fortune['time']) - 86400);
                break;
            case 3:
                self::$arr['fortune_data']['start_time'] = date('Y-m', $fortune['time']);
                self::$arr['fortune_data']['end_time'] = date('Y-m', strtotime($goods['expire_time'], $fortune['time']) - 86400);
                break;
            case 4:
                self::$arr['fortune_data']['start_time'] = date('Y', $fortune['time']);
                self::$arr['fortune_data']['end_time'] = date('Y', strtotime($goods['expire_time'], $fortune['time']) - 86400);
                break;
            default:
                self::$arr['fortune_data'] = [];
                break;

        }
        return self::$arr;
    }

    /**
     * 地图详情
     * @param array $data
     * @return array
     * @throws Exception
     */
    public static function MapRead($data)
    {
        //获取商品信息
        $maps_goods = current((new OrdersGoods())->batchGetInfo([$data['id']])) ?: [];
        $rids = [];
        $gids = [];
        foreach ($maps_goods as $order_good)
        {
            $og = json_decode($order_good, true);
            $rids[] = $og['rid'];
            $gids[] = $og['gid'];
        }
        self::$arr['goods_data'] = (new Goods())->batchGetInfoByIds($gids);
        if (in_array(19, $gids))
        {
            self::$arr['goods_name'] = '幸运之城';
            self::$arr['goods_data_des'] = '幸运之城全内容打包解锁';
        } else
        {
            self::$arr['goods_data_des'] = implode('、', array_column(self::$arr['goods_data'], 'name'));
        }
        $price = 0;
        foreach (self::$arr['goods_data'] as $good)
        {
            $price += $good['now_prize'];
        }
        self::$arr['goods_data_price'] = $price;
        //获取档案信息
        self::$arr['recode_data'] = (new Recode())->batchGetInfoByIds($rids);
        return self::$arr;
    }

    /**
     * 视频详情
     * @param array $data
     * @return array
     * @throws Exception
     */
    public static function VideoRead($data)
    {
        //获取商品信息
        self::$arr['goods_data'] = (new Goods())->batchGetInfoByIds($data['gid']);
        $video_title = (new ZhunleCourse())->batchGetInfoByGIds($data['gid'], ['id', 'title']);
        $video_title = $video_title ? current($video_title) : [];
        self::$arr['video_id'] = (int)$video_title['id'];
        self::$arr['goods_data_des'] = $video_title['title'];
        return self::$arr;
    }

    /**
     * vip详情
     * @param $data
     * @return array
     * @throws Exception
     */
    public static function VipRead($data)
    {
        //获取商品信息
        self::$arr['goods_data'] = (new Goods())->batchGetInfoByIds($data['gid']);
        $goods = self::$arr['goods_data'] ? current(self::$arr['goods_data']) : [];
        $goods_extend = $goods['extend'] ? json_decode($goods['extend'], true) : [];
        //获取商品描述
        if (Util::CheckAppVersion('2.9.7') === '大' && Util::IsIos())
        {
            self::$arr['goods_data_des'] = $goods_extend['ios_goods_des'] ?? '';
        } else
        {

            self::$arr['goods_data_des'] = $goods_extend['goods_des'] ?? '';
        }
        //获取用户当前vip有效时间
        $extend = $data['extend'] ? json_decode($data['extend'], true) : [];
        $vip_start_time = $extend['vip_start_time'] ?? 0;
        $vip_end_time = $extend['vip_end_time'] ?? 0;
        self::$arr['vip_start_time'] = $vip_start_time ? date('Y-m-d', $vip_start_time) : '';
        self::$arr['vip_end_time'] = $vip_end_time ? date('Y-m-d', $vip_end_time) : '';
        return self::$arr;
    }

    /**
     * 获取用户vip现在有效时间和截止有效时间和会员状态
     * @param int $uid
     * @param $gid
     * @return array
     * @throws Exception
     */
    public static function getUserVipTime($uid, $gid)
    {
        $nowTime = time();
        //获取会员商品信息
        $goods = (new Goods)->getInfoById($gid);
        //获取用户vip信息
        $UserVip = (new ZlUserVip())->getUserVipFromCache($uid);
        //进行开通
        $arr['vip_start_time'] = $nowTime;
        $arr['vip_end_time'] = strtotime($goods['expire_time']) - 60;
        //若当前vip有效则进行续时
        switch (true)
        {
            case $UserVip && $UserVip['is_valid']://已是会员并有效,延长会员时效
                $arr['vip_start_time'] = max($UserVip['end_time'], $nowTime);
                $arr['vip_end_time'] = strtotime($goods['expire_time'], $arr['vip_start_time']) - 60;
                $arr['is_valid'] = 1;
                break;
            case $UserVip && !$UserVip['is_valid']://已是会员但是无效，重新刷新时效
                $arr['is_valid'] = 0;
                break;
        }
        return $arr;
    }

    /**
     * 付款后更新vip信息
     * @param $orders
     * @param $gid
     * @return bool
     */
    public static function UpdateVipStatus($orders, $gid)
    {
        try
        {
            $UserVip = self::getUserVipTime($orders['uid'], $gid);//返回时间为时间戳
            //订单记录会员开通的时间
            $extend = [];
            if (!empty($orders['extend']))
            {
                if (is_array($orders['extend']))
                {
                    $extend = $orders['extend'];
                } else
                {
                    $extend = json_decode($orders['extend'], true);
                }
            }
            $extend['vip_start_time'] = $UserVip['vip_start_time'];
            $extend['vip_end_time'] = $UserVip['vip_end_time'];
            (new Orders())->UpdateData($orders['order_id'], ['extend' => json_encode($extend, true)]);
            //获取用户vip有效时间
            $ZlUserVipModel = new ZlUserVip();
            $isSend = false;    // 是否赠送权益
            $isUpdateVipSendBoon = true; // 是否VIP升级自动赠送权益
            //续费vip或激活vip
            if (isset($UserVip['is_valid']))
            {
                if (in_array($gid, [20106, 20105, 20112, 20111], false))
                {
                    $updateData = ['is_valid' => 1, 'end_time' => $UserVip['vip_end_time'], 'update_time' => time(), 'apple_status' => 1];
                } else
                {
                    $updateData = ['is_valid' => 1, 'end_time' => $UserVip['vip_end_time'], 'update_time' => time()];
                }
                $res = $ZlUserVipModel->UpdateData($orders['uid'], $updateData);
                //用户激活会员时，赠送优惠券
                if (!$UserVip['is_valid'])
                {
                    $isUpdateVipSendBoon = false;
                    $isSend = true;
                    // 消息中心添加信息
                    $messageData = [
                        'status' => 0,
                        'is_read' => 0,
                        'range' => $orders['uid'],
                        'type' => 2,
                        'reid' => 0,
                        'cid' => 0,
                        'title' => '会员服务激活成功',
                        'content' => '恭喜您已成功激活准了会员服务目前有效期至【' . date('Y-m-d', $UserVip['vip_end_time']) . '】',
                        'link' => 'vip_center?from=1',
                        'target' => 'local',
                        'create_time' => time(),
                    ];
                } else
                {//续费
                    // 消息中心添加信息
                    $messageData = [
                        'status' => 0,
                        'is_read' => 0,
                        'range' => $orders['uid'],
                        'type' => 2,
                        'reid' => 0,
                        'cid' => 0,
                        'title' => '会员服务续费成功',
                        'content' => '恭喜您已成功续费准了会员服务目前有效期至【' . date('Y-m-d', $UserVip['vip_end_time']) . '】',
                        'link' => 'vip_center?from=1',
                        'target' => 'local',
                        'create_time' => time(),
                    ];
                }
            } else
            {//开通vip
                if (in_array($gid, [20106, 20105, 20112, 20111], false))
                {
                    $saveData = ['end_time' => $UserVip['vip_end_time'], 'uid' => $orders['uid'], 'create_time' => time(), 'vip_level' => 1, 'is_valid' => 1, 'apple_status' => 1];
                } else
                {
                    $saveData = ['end_time' => $UserVip['vip_end_time'], 'uid' => $orders['uid'], 'create_time' => time(), 'vip_level' => 1, 'is_valid' => 1];
                }
                $res = $ZlUserVipModel->SaveData($saveData);
                // 消息中心添加信息
                $messageData = [
                    'status' => 0,
                    'is_read' => 0,
                    'range' => $orders['uid'],
                    'type' => 2,
                    'reid' => 0,
                    'cid' => 0,
                    'title' => '会员服务开通成功',
                    'content' => '恭喜你开通准了会员服务,有效期至【' . date('Y-m-d', $UserVip['vip_end_time']) . '】，点击查看详情>>>',
                    'link' => 'vip_center?from=1&need_auth=2',
                    'target' => 'local',
                    'create_time' => time(),
                ];
                //赠送优惠券
                $isSend = true;
                $isUpdateVipSendBoon = false; // 是否VIP升级自动赠送权益
            }
            //发送消息
            try
            {
                (new MessageCenter())->pushMessage($orders['uid'], $messageData);
            }
            catch (Exception $e)
            {
                print("会员 写入消息中心失败: " . $e->getMessage() . PHP_EOL);
                Log::write("会员 写入消息中心失败: " . $e->getMessage() . PHP_EOL, 'error');
            }

            $queueData = [
                'orders' => $orders,
                'gid' => $gid,
                'isUpdateVipSendBoon' => $isUpdateVipSendBoon,
                'isSend' => $isSend,
            ];
            // 更新星力值及激活会员或开通会员赠送权益 写入队列
            Util::queuePush('star_num_update_send_boon', $queueData, 10);
            // // 更新星力值
            // OrdersLibCacheModel::UpdateVipBoon($orders, $goods, $isUpdateVipSendBoon);
            // // 激活会员或开通会员赠送权益
            // if ($isSend)
            // {
            //     $ZlVipBoonModel->vipBoonSend($orders['uid']);
            // }
        }
        catch (Exception $e)
        {
            Log::write('更新会员信息失败:' . $e->getMessage() . '文件错误行数：' . $e->getLine() . PHP_EOL . '订单信息：' . var_export($orders, true) . PHP_EOL . '商品信息：' . var_export($gid, true), 'error');
            $res = false;
        }
        return $res;
    }

    /**
     * 用户付款后更新星力值
     * @param $orders
     * @param int $gid
     * @param bool $isSend 是否升级自动赠送权益
     */
    public static function UpdateVipBoon($orders, $gid, bool $isSend)
    {
        try
        {
            $ZlUserVipModel = new ZlUserVip();
            //下单时vip需要更新星力值
            $ZlUserVipModel->updateStarNum((int)$orders['uid'], (int)$orders['order_type'], $orders['total_amount'], $gid, $isSend);
        }
        catch (Exception $e)
        {
            Log::write('更新会员星力值失败:' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】' . PHP_EOL . '订单信息：' . var_export($orders, true) . PHP_EOL . '商品信息：' . var_export($gid, true), 'error');
        }
    }

    /**
     * 获取用户所有未支付的订单，判断是否超时值未支付
     */
    public static function getUserAllUnpaidOrder($uid)
    {
        $orderModel = new OrdersCacheModel();
        try
        {
            $orders = $orderModel->where(['uid' => $uid, 'status' => 0, 'is_del' => 0])->column('uid,order_id,create_time,extend,fid,status,gid,order_type', 'id');
        }
        catch (Exception $e)
        {
            $orders = [];
        }
        foreach ($orders as $order)
        {
            $close_time = $order['create_time'] + config('zhunleorder')['close_order_time'];//30分钟未支付关闭订单
            //若订单超时未支付则更改状态,并退回优惠券
            if ($close_time <= time())
            {
                self::CloseOrder($order);
            }
        }
        return true;
    }

    /**
     * 关闭订单，并退回优惠券和还原库存(订单未支付)
     * @param object $order
     * @param int $returnUser 关闭订单操作人员 1=>系统超时关闭 2=>用户主动关闭 3=>主播主动关闭
     * @param string $cancelReason 关闭原因
     */
    public static function CloseOrder($order, $returnUser = 1, $cancelReason = ''): void
    {
        $orderModel = new OrdersCacheModel();
        $UserCouponModel = new UserCoupon();
        $mallGoodsAttrModel = new MallGoodsAttr();
        $order['order_type'] = (int)$order['order_type'];
        $now_time = time();
        Db::startTrans();
        try
        {
            //更新缓存
            $orderModel->UpdateData($order['order_id'], ['status' => 2], 2);
            $extend = $order['extend'] ? json_decode($order['extend'], true) : [];
            $order_coupon = $extend['coupon'] ?? '';//获取订单的优惠券id
            //退回优惠券
            if ($order_coupon)
            {
                $coupon_info = $UserCouponModel->getInfoById($order_coupon['cid']);
                //若当前优惠券在有效期内则退回，否则更改为已过期
                if ($now_time <= $coupon_info['end_time'] || $coupon_info['end_time'] == 0)
                {
                    $UserCouponModel->use_return($order['uid'], $order_coupon['cid'], ['status' => 0, 'oid' => 0, 'update_time' => time()]);
                } else
                {
                    $UserCouponModel->use_return($order['uid'], $order_coupon['cid'], ['status' => 3, 'oid' => 0, 'update_time' => time()]);
                }
            }
            //商城订单需要还原库存数量
            if ($order['order_type'] === 14)
            {
                $extend = $orderModel->getMallExtend($order);
                $mallNum = $extend['num'];
                $mallGoodsAttrModel->where('id', $order['gid'])->setInc('inventory', $mallNum);
                $mallGoodsAttrModel->UpdateCache($order['gid']);
            } elseif ($order['order_type'] === 15)
            {
                $questionGoodsModel = new ZlcrCustomReportGoods();
                //库存还原
                $questionGoodsModel->resetGoodsIndexNum();
            }//解答占位去除
            elseif (in_array($order['order_type'], [19, 20, 21], true))
            {
                $liveInfo = $extend['live_info'] ?? [];
                $answerModel = null;
                switch ($returnUser)
                {
                    case 2:
                        $status = 8;
                        break;
                    case 3:
                        $status = 9;
                        break;
                    default:
                        $status = 7;
                        break;
                }
                switch ($order['order_type'])
                {
                    case 19:
                        $answerModel = new LiveAnswerAstroRecord();
                        break;
                    case 20:
                        $answerModel = new LiveAnswerDiceRecord();
                        break;
                    case 21:
                        $answerModel = new LiveAnswerTarotRecord();
                        break;
                }
                $answerModel->updateAnswerRequest(['id' => $order['fid'], 'uid' => $order['uid'], 'status' => $status, 'cancel' => time(), 'cancel_reason' => $cancelReason, 'live_id' => $liveInfo['live_id'], 'live_record_id' => $liveInfo['live_record_id'], 'answer_end_rid' => $liveInfo['live_record_id']]);
            } elseif ($order['order_type'] === 22)
            {
                $questionModel = new Zlcr2UserQuestion();
                $res = $questionModel->getQuestionType((int)$order['gid']);
                $status = 3;
                //问题处理
                if ($res['type'] === 3)
                {
                    //若订单为进行中状态则需要退款
                    $question = $questionModel->getInfoById($order['fid'], ['status']);
                    if ((int)$order['status'] === 1 && (int)$question['status'] === 2)
                    {
                        $status = 10;
                        $res = $orderModel->refund($order['uid'], $order, $order['total_amount']);
                        if ($res['status'] === false)
                        {
                            throw new ApiException('问答退款失败');
                        }

                        // 消息中心添加信息
                        $messageData = [
                            'status' => 0,
                            'is_read' => 0,
                            'range' => $order['uid'],
                            'type' => 2,
                            'reid' => 0,
                            'cid' => 4,
                            'title' => '订单取消通知',
                            'content' => "订单取消成功，你开启的悬赏问答，订单编号：{$order['order_id']}已取消成功，资金将原路返还，请注意查收",// 消息中心跳转地址
                            'link' => "order_details?order_id={$order['order_id']}&order_type={$order['order_type']}",
                            'target' => 'local',
                            'create_time' => time(),
                        ];
                        (new MessageCenter())->pushMessage($order['uid'], $messageData);
                    }
                    $questionModel->UpdateData($order['fid'], ['status' => $status, 'success_time' => $now_time]);
                } elseif ($res['type'] === 2)//追问处理
                {
                    (new Zlcr2UserAddQuestion())->UpdateData($order['fid'], $extend['history']['id'], ['status' => 3]);
                }
            }
            Db::commit();
        }
        catch (Exception $e)
        {
            Log::write($order['order_id'] . '订单超时关闭失败：' . $e->getMessage() . PHP_EOL . '错误行数：' . $e->getLine(), 'error');
            Db::rollback();
        }
    }

    /**
     * 私域 H5 关闭订单
     * @param array $order
     */
    public static function privateAreaCloseOrder(array $order): void
    {
        $privateAreaOrderModel = new PrivateAreaOrders();
        $questionInfoModel = new PrivateAreaQuestionInfo();
        $time = time();
        // 更新订单状态
        $res = $privateAreaOrderModel->updateOrderInfo($order['order_id'], [
            'status' => 3,
            'success_time' => $time,
        ]);
        if (!$res)
        {
            return;
        }
        // 更新咨询 信息
        $questionInfoModel->updateUserQuestionData($order['qid'], [
            'success_time' => $time,
            'status' => 3,
        ]);
    }

    /**
     * 私域 H5实物销售 关闭订单
     * @param array $order
     * @throws \Exception
     */
    public static function privateAreaMallCloseOrder(array $order): void
    {
        $privateAreaOrderModel = new PrivateAreaOrders();
        $time = time();
        // 更新订单状态
        $res = $privateAreaOrderModel->updateOrderInfo($order['order_id'], [
            'status' => 3,  // 关闭订单
            'success_time' => $time,
        ]);
        if (!$res)
        {
            return;
        }
        $privateAreaMallGoodsAttrModel = new PrivateAreaMallGoodsAttr();
        $goodAttrDetail = $privateAreaMallGoodsAttrModel->getInfoById($order['qid']);
        // 扣除增加
        $privateAreaMallGoodsAttrModel->UpdateData($order['qid'], ['inventory' => $goodAttrDetail['inventory'] + 1]);
    }

    /**
     * 更新准币订单数量和状态(支付成功后)
     * @param string $order_id
     * @param int $order_type 流水类型1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8会员购买 9准币充值 10许愿充值 11赠送
     * @param string $total_amount
     * @param int $uid
     * @param int $consumer_type // 1=>充值 2=>消费,3=>赠送,4=>准币购买申请退款,5=>打赏,6=>苹果充值申请退款
     * @param int $trade_type 支付方式   1=>支付宝,2=>微信支付，3=>余额，4=>准币，5=>京东，6=>微信JSAPI，7=>支付宝手机网页，8=>微信H5，9=>苹果支付，13=>微信小程序支付,14=>广告解锁，15=>微信浏览器打开H5页面使用JSAPI支付
     * @return bool
     */
    public static function UpdateCoin($order_id, $order_type, $total_amount, $uid = 0, $consumer_type = 1, $trade_type = 0, $transactionId = '')
    {
        //流水类型1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8会员购买 9准币充值 10许愿充值
        $ZhunleUserAccountRecordModel = new ZhunleUserAccountRecord();
        $ZhunleUserAccountModel = new ZhunleUserAccount();
        Db::startTrans();
        try
        {
            $coin_num = self::getOrderCoinNum($total_amount);
            //获取准币订单信息
            $records = $ZhunleUserAccountRecordModel->where(['order_id' => $order_id])->column('id,rid,gid,total_amount,order_type,consumer_type,coin_num,uid,order_id');
            if (!$records)
            {
                $recode = (new \App\Models\Api\Recode())->getSelfRecode(['uid' => $uid]);
                $user_coin = self::getCoinNum($uid);
                if (in_array($consumer_type, [1, 3, 4], true))
                {
                    $balance = $coin_num + $user_coin;
                } else
                {
                    $balance = $user_coin - $coin_num;
                }
                $records = [
                    'status' => 0,
                    'account' => 0,
                    'uid' => $uid,
                    'consumer_type' => $consumer_type,
                    'order_type' => $order_type,
                    'gid' => 0,
                    'pray_date' => 0,
                    'rid' => $recode['id'],
                    'total_amount' => $total_amount,//实际收入
                    'pay_price' => $total_amount,//用户支付
                    'coin_num' => $coin_num,
                    'balance' => $balance,
                    'order_id' => $order_id,
                    'trade_type' => $trade_type,
                    'create_time' => time(),
                    'transaction_id' => $transactionId,
                ];
                //增加准币明细记录
                $records['id'] = (new ZhunleUserAccountRecord())->SaveData($uid, $records);
            } else
            {
                $records = current($records);
            }

            if ($consumer_type === 1)
            {
                $coin_num = $records['coin_num'];
            }
            $ZhunleUserAccountModel->updateCoinInfo($records['uid'], $consumer_type, $records, $coin_num, $trade_type, $transactionId);
            //更改许愿状态缓存
            if ((int)$records['order_type'] === 10)
            {
                (new ZhunleUserAccountRecord())->prayAdd((int)$records['uid'], (int)$records['rid'], date('Ymd'), [$records['gid'] => $records['gid']]);
            }
            $startTransStatus = true;
            Db::commit();
        }
        catch (Exception $e)
        {
            $startTransStatus = false;
            Log::write($order_id . '更新准币状态和数量失败：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】');
            Db::rollback();
        }

        Util::logRealTimeAnalyzeWrite(3, [
            'pay_notify_uid' => $records['uid'] ?? '',
            'pay_notify_order_id' => $order_id,
            'pay_notify_order_type' => $order_type,//1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8=>会员购买 9=>准币充值 10=>祈福充值 11=>赠送
            'pay_notify_total_amount' => $total_amount,
            'pay_notify_gid' => $records['gid'] ?? '',
        ]);

        return $startTransStatus;
    }

    /**
     * 准币支付，检测准币是否充足，并生成明细
     * @param array $orders 一维数组
     * @param string $coinPassword 支付密码
     * @param int $consumer_type 2=>消费 5=>打赏
     * @param string $extend
     * @return bool|int|string
     * @throws ApiException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function checkCoinPayInfo($orders = [], $coinPassword = '', $consumer_type = 2, $extend = '', $verifyPassword = true)
    {
        //检测准币是否充足
        $userCoinInfo = (new ZhunleUserAccount())->getUserCoinFromCache($orders['uid']);
        $param['coin_num'] = OrdersCacheModelLib::getOrderCoinNum($orders['total_amount']);
        (new ZhunleUserAccount())->verifyCoinPay($userCoinInfo, $coinPassword, $param['coin_num'], false, $verifyPassword);
        $user_coin = (int)($userCoinInfo['balance'] ?? 0);// 准币余额

        //获取准币订单信息
        $records = (new ZhunleUserAccountRecord())->where(['order_id' => $orders['order_id']])->field('id')->find();
        if (!$records)
        {
            //生成明细
            $insertData = [
                'status' => 0,
                'account' => 0,
                'uid' => $orders['uid'],
                'consumer_type' => $consumer_type,
                'order_type' => $orders['order_type'],
                'gid' => $orders['gid'],
                'pray_date' => 0,
                'total_amount' => $orders['total_amount'],
                'coin_num' => $param['coin_num'],
                'balance' => $user_coin - $param['coin_num'],
                'order_id' => $orders['order_id'],
                'trade_type' => 4,
                'create_time' => time(),
                'extend' => $extend,
            ];
            //增加准币明细记录
            return (new ZhunleUserAccountRecord())->SaveData($orders['uid'], $insertData);
        }
        return $records['id'];
    }

    /**
     * 关闭2019年运购买
     * @param $start_time
     * @throws ApiException
     */
    public static function CloseOrderFortuneTime($order_type, $start_time)
    {
        //若购买的年运时间为2019，则提示不允许购买
        if ((int)$order_type === 4)
        {
            if (Util::is_strtotime($start_time) === true)
            {
                if (date('Y', $start_time) === '2019')
                {
                    throw new ApiException('2019已经过去，建议您直接购买2020年运势哦！为防止您误购，2019暂时关闭购买，春节后恢复哦~');
                }
            } else
            {
                if (date('Y', strtotime($start_time)) === '2019')
                {
                    throw new ApiException('2019已经过去，建议您直接购买2020年运势哦！为防止您误购，2019暂时关闭购买，春节后恢复哦~');
                }
            }
        }
    }

    /**
     * 验证下单请求参数
     * @param int $uid
     * @param array $param
     * @return bool
     * @throws ApiException
     */
    private static function check_pay_unlock_param(int $uid, array $param)
    {
        if (!in_array($param['order_type'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35], true))
        {
            throw new ApiException('未知的商品');
        }
        if (empty($param['history_id']) && Goods::isBaziJsjr($param['order_type'], $param['gid']))
        {
            return '服务异常，请稍后重试～';
        }
        //检测生时校正信息是否正确
        if ($param['order_type'] === 1)
        {
            if (!$param['reid'])
            {
                throw new ApiException('请填写事件');
            }
            $rec = (new Rectification())->getInfoById($param['reid']);
            if (!$rec)
            {
                throw new ApiException('请填写事件');
            }
            if ($rec['uid'] != $uid)
            {
                throw new ApiException('服务异常');
            }
            //状态 -1 已删除 ||  0未支付  ||  1等待矫正  || 2矫正成功  ||3矫正失败  ||  4 支付失败 || 5支付关闭 || 6退款成功
            if ($rec['status'])
            {
                throw new ApiException('当前校正信息已生成订单，请重新确认');
            }
        }

        if ((in_array($param['order_type'], [2, 3, 4, 12, 17], true) || $param['gid'] === 91) && !$param['start_time'])
        {
            return '请选择时间';
        }

        //验证支付方式
        if (!in_array($param['trade_type'], [1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15], true))
        {
            throw new ApiException('当前支付方式不支持');
        }

        if ($param['order_type'] === 16 && !$param['history_id'])
        {
            throw new ApiException('请求异常');
        }

        $recodeModel = new Recode();
        //检测档案信息是否正确
        if (in_array($param['order_type'], [1, 2, 3, 4, 5, 7, 12, 13, 15, 17, 23, 25, 26, 27, 28], true))
        {
            if (!$param['rid'])
            {
                throw new ApiException('请选择档案');
            }
            //判断档案是否存在
            $recode = $recodeModel->getInfoById($param['rid']);
            if (!$recode || (int)$recode['status'] === 0 || (int)$recode['uid'] !== $uid)
            {
                throw new ApiException('无此档案，请重新确认');
            }

            self::$recodeInfo1 = $recode;
            if (in_array($param['order_type'], [17, 25]) || ($param['order_type'] === 15 && $param['rid2']))
            {  // 验证档案2信息
                //判断档案是否存在
                $recode2 = $recodeModel->getInfoById($param['rid2']);
                if (!$recode2 || (int)$recode2['status'] === 0 || (int)$recode2['uid'] !== $uid)
                {
                    throw new ApiException('无此档案，请重新确认');
                }
                self::$recodeInfo2 = $recode2;
            }
        }
        return true;
    }

    ############################################## todo 版本2.7.0之前的下单逻辑 ################################################

    /**
     * 下单参数处理 v12,v13
     * @param $uid
     * @param $param
     * @return mixed
     * @throws ApiException
     */
    public static function pay_order_param_format(int $uid, array $param, bool $isFree = false)
    {
        $cache = Cache::get('order_pay_insert:' . $uid);
        if ($cache)
        {
            throw new ApiException('操作频繁，请稍后再试', 200, 0);
        }
        Cache::set('order_pay_insert:' . $uid, true, 5);
        $param['gids'] = explode(',', $param['gids']);
        $param['gid'] = $gid = (int)current($param['gids']);
        $param['order_type'] = (int)$param['order_type'];
        $param['trade_type'] = (int)$param['trade_type'];
        $param['openid'] = $param['openid'] ?? '';//h5支付请求参数
        $param_info = $param['param'] ? json_decode($param['param'], true) : '';
        if (!$param_info)
        {
            throw new ApiException('缺少信息，请重试');
        }
        $param['cid'] = $param_info['cid'] ?? 0;
        $param['rid'] = $param_info['rid'] ?? 0;
        $param['rid2'] = $param_info['rid2'] ?? 0;
        $param['reid'] = $param_info['reid'] ?? 0;
        $param['start_time'] = $param_info['start_time'] ?? '';//日月年购买时间
        $param['problem'] = $param_info['problem'] ?? '';//定制报告自定义问题
        $param['history_id'] = $param_info['history_id'] ?? '';//塔罗记录id
        //验证下单参数
        self::check_pay_unlock_param($uid, $param);
        //塔罗记录获取
        if ($param['order_type'] === 16)
        {
            $tarot = (new ZlTarotRecords())->getInfoById($param['history_id']);
            if (!$tarot || $tarot['uid'] != $uid || $tarot['gid'] != $gid)
            {
                throw new ApiException('商品不存在');
            }
            if ($tarot['order_num'])
            {
                $ordersInfo = (new Orders())->OrderRidsInfo($tarot['order_num']);
                if (!in_array((int)$ordersInfo['status'], [0, 2, 3], true))
                {
                    throw new ApiException('塔罗记录状态异常，稍后重试～');
                }
                if ((int)$ordersInfo['status'] === 0)
                {
                    $param['order_num'] = $ordersInfo['order_id'];
                    $param['oid'] = $ordersInfo['id'];
                }
                // throw new ApiException('塔罗记录已生成订单，请前往订单进行查看');
            }
        }
        //运势下单检验下单时间格式，并返回时间戳类型的时间
        if (in_array($param['order_type'], [2, 3, 4, 12, 17], true))
        {
            $param['start_time'] = Util::FormatFortuneTime($param['order_type'], $param['start_time'], $gid)['start_time'];
        }
        //判断商品是否已经购买
        $res = self::CheckOrder($uid, $param['gids'], $param['order_type'], $param['rid'], $param['start_time'], $param['reid'], $param['rid2']);
        if ($res['msg'])
        {
            throw new ApiException($res['msg']);
        }

        //todo 暂时关闭2019年运购买
//        \App\Models\CacheModel\OrdersLib::CloseOrderFortuneTime($param['order_type'], $param['start_time']);

        //获取购买的商品信息,优惠券信息，活动信息，和参与优惠后的最终价格【'goods'， 'price'， 'active'， 'coupon'】
        $goods_info = self::OrderGoodsInfo($uid, $param, $isFree);
        $param['price'] = $goods_info['price'];
        //获取订单类型和描述语和订单编号
        $order_type_des = self::createOrderId($param['order_type'], $param['gid']);
        $param['pay_des'] = $order_type_des['pay_des'];
        $param['out_trade_no'] = $order_type_des['order_id'];

        //准币支付时检测准币是否充足并添加明细记录
        if ($param['trade_type'] === 4)
        {
            //获取当前商品所需准币
            $param['coin_num'] = self::getOrderCoinNum($param['price']);
            //2.5.0和2.5.3版本之间验证准币密码
            $userCoinInfo = (new ZhunleUserAccount())->getUserCoinFromCache($uid);
            $user_coin = $userCoinInfo ? (int)$userCoinInfo['balance'] : 0;
            if (Util::CheckAppVersion('2.5.0') !== '小' && Util::CheckAppVersion('2.5.3') === '小')
            {   // 2.5.0版本及以上  进行支付密码验证
                (new ZhunleUserAccount())->verifyCoinPay($userCoinInfo, $param['password'], $param['coin_num']);
            } else
            {
                //检测余额是否足够
                if ($user_coin < $param['coin_num'])
                {
                    throw new ApiException('准币不足，请前去充值', 200, 410);
                }
            }

            //生成明细
            $insertData = [
                'status' => 0,
                'account' => 0,
                'uid' => $uid,
                'consumer_type' => 2,
                'order_type' => $param['order_type'],
                'gid' => $gid,
                'pray_date' => 0,
                'total_amount' => $param['price'],
                'coin_num' => $param['coin_num'],
                'balance' => $user_coin - $param['coin_num'],
                'order_id' => $param['out_trade_no'],
                'trade_type' => $param['trade_type'],
                'create_time' => time(),
            ];
            //增加准币明细记录
            (new ZhunleUserAccountRecord())->SaveData($uid, $insertData);
        }
        //下单使用的商户配置id
        $accountInfo = self::getOrderAccountType($param['out_trade_no'], $param['trade_type']);
        $param['account'] = $accountInfo['account'];
        //生成购买商品时的活动和优惠券数据
        $param['extend'] = [];
        //历史记录信息
        if ($param['history_id'])
        {
            $param['extend']['history']['id'] = $param['history_id'];
            if ($param['order_type'] === 16)
            {
                $param['extend']['history']['content'] = $tarot['question'] ?? '';
            }
        }
        //活动信息
        if ($goods_info['active'])
        {
            $param['extend']['active'] = $goods_info['active'];
        }
        //优惠券信息
        if ($goods_info['coupon'])
        {
            $param['extend']['coupon'] = $goods_info['coupon'];
        } else
        {
            $param['cid'] = 0;
        }

        if ($param['order_type'] === 17)
        {
            $param['extend']['synastry_report'] = [
                'rid1' => $param['rid'],
                'rid2' => $param['rid2'],
                'relation_id' => $goods_info['goods']['relation_id'],
                'srid' => $goods_info['goods']['srid'],
                'start_time' => $param['start_time'],
                'expire_time' => $goods_info['goods']['good_list'][$gid]['expire_time'],
            ];
        }
        $param['extend'] = \GuzzleHttp\json_encode($param['extend'], JSON_UNESCAPED_UNICODE);
        return $param;
    }

    /**
     * 获取下单商品信息
     * @param int $uid
     * @param $param
     * @return array
     * @throws ApiException
     */
    public static function OrderGoodsInfo(int $uid, $param, bool $isFree)
    {
        //获取购买的商品信息
        if ($param['order_type'] === 15)//获取定制报告的商品信息
        {
            $goods = (new ZlcrCustomReportGoods())->batchGetInfoByIds($param['gids']);
            $goodsIds = array_column($goods, 'id');
            $diffType = array_merge(array_diff($goodsIds, $param['gids']), array_diff($param['gids'], $goodsIds));
            if ($diffType)
            {
                throw new ApiException('商品不存在');
            }
            $oneGoods = current($goods);
            //定制报告自定义问题参数校验
            if ($oneGoods['qtype'] == 2 && !$param['problem'])
            {
                throw new ApiException('请填写问题', 200, 0);
            }
            //计算当前商品原价
            $price = array_sum(array_map(static function ($val)
            {
                return $val['price'];
            }, $goods));
            //若当前是非合盘报告，则判断rid2是否存在
            if ($oneGoods['is_merge'] && !self::$recodeInfo2)
            {
                throw new ApiException('请选择档案');
            }
        } elseif ($param['order_type'] === 17)
        {
            $GoodsCacheModel = new GoodsCacheModel();
            $goodsInfoData = $GoodsCacheModel->GoodsTypeInfo($param['order_type']);
            $gid = (int)current($param['gids']);
            foreach ($goodsInfoData as $srid => $goodInfoItem)
            {
                $relationId = null;
                foreach ($goodInfoItem['relation_list'] as $relationIdKey => $relationItem)
                {
                    if (in_array($gid, $relationItem['gids'], true))
                    {
                        $goodInfoItem['gid'] = $gid;
                        $relationId = $relationIdKey;
                    }
                }
                if (!$relationId) continue;

                $goodInfoItem['relation_id'] = $relationId;

                $goods = $goodInfoItem;
                $price = $goodInfoItem['good_list'][$gid]['now_prize'];
                break;
            }
        } else
        {
            $goods = (new Goods())->batchGetInfoByIds($param['gids']);
            $goodsType = array_unique(array_column($goods, 'type'));
            $diffType = array_diff([$param['order_type']], $goodsType);
            if ($diffType)
            {
                throw new ApiException('商品不存在');
            }
            //计算当前商品原价
            $price = array_sum(array_map(static function ($val)
            {
                return $val['now_prize'];
            }, $goods));
        }

        //获取除定制报告外的商品优惠价格，定制报告无活动无优惠
        // 定制报告参与官方折扣活动 2021-05-07
        if ($goods)
        {
            //获取当前用户的内部VIP登等级
            $vipLevel = Util::getVips($uid, $param['order_type'], $param['rid']);
            //获取vip购买商品的价格
            if ($vipLevel)
            {
                $price = self::innerVipGoodPrice($uid, $vipLevel, $param) ?: $price;
            } else
            {
                //计算优惠后的商品价格
                $resPrice = self::favorablePrice($uid, $param['order_type'], $param['cid'], $param['start_time'], $price, $param['gids'], $isFree);
                $price = $resPrice['price'];
            }
        } else
        {
            throw new ApiException('商品不存在', 200, 0);
        }
        return [
            'goods' => $goods,
            'price' => sprintf('%.2f', $price),
            'active' => $resPrice['active'] ?? [],
            'coupon' => $resPrice['coupon'] ?? [],
        ];
    }

    /**
     * 获取优惠后的价格
     * @param int $uid 用户id
     * @param int $order_type 商品类型
     * @param int $cid 优惠券id
     * @param string $start_time 运势购买开始时间
     * @param string $price 商品原价
     * @param array $gids 购买的商品id
     * @return string[]
     * @throws ApiException
     */
    public static function favorablePrice($uid, $order_type, $cid, $start_time, $price, $gids, $isFree)
    {
        $orPrice = $price;
        //获取当前商品活动信息，先计算扣除活动后的价格
        $header = request()->header();
        // APP 下单
        if (in_array($header['app-type'], ['ios', 'android', 'ipad'], true))
        {
            //重组活动的运势时间
            if ($start_time && in_array($order_type, [2, 3, 4, 12], true))
            {
                $start_time_date = date('Y-m-d', $start_time);
            } else
            {
                $start_time_date = '';
            }
            $activating = (new ZhunleActivity())->getActivingGood($order_type, current($gids), $start_time_date);
        } else
        {   // 小程序下单没有活动
            $activating = [];
        }
        //计算参与活动后的信息
        if ($activating)
        {
            //算出折扣抵消价格
            // $active_discount_price1 = ((int)round(($price * (1 - $activating['attr']) * 100))) / 100;
            $tempPrice = $price * (1 - $activating['attr']);
            $active_discount_price = sprintf("%u", bcmul($tempPrice, 100)) / 100;
            //算出活动后需要支付的金额
            $price -= $active_discount_price;
            $active_data = [
                'cid' => 0,
                'attr' => $activating['attr'],
                'ac_id' => $activating['id'],
                'name' => $activating['name'],
                'title' => $activating['des'],
                'discount_price' => $active_discount_price,
            ];
        }

        //获取用户可用的优惠券
        $CouponModel = new UserCoupon();
        $coupons = $CouponModel->getUsableList($uid, $gids, $price);
        $coupons_cid = $coupons ? array_column($coupons, 'id') : 0;
        if ($coupons_cid && $cid)
        {
            if (in_array($cid, $coupons_cid, false))
            {
                //获取当前优惠券信息
                $coupon = $CouponModel->getInfoById($cid);
                //1折扣  2立减劵 3满减 4随机卷,获取算出折扣抵消价格
                switch (true)
                {
                    case $coupon['type'] == 1: //若是折扣，直接扣除,
                        $coupon_discount_price = (string)($price * (1 - $coupon['discount']) * 100);
                        $coupon_discount_price = (int)$coupon_discount_price / 100;
                        break;
                    case in_array($coupon['type'], [2, 3])://若是满减立减，则减去活动价格
                        //若折扣金额大于商品金额，则抵消商品对等金额
                        if ($coupon['discount'] > $price)
                        {
                            $coupon['discount'] = $price;
                        }
                        $coupon_discount_price = (string)($coupon['discount'] * 100);
                        $coupon_discount_price = (int)$coupon_discount_price / 100;
                        break;
                    case $coupon['type'] == 4://随机券
                        //若折扣金额大于商品金额，则抵消商品对等金额
                        if ($coupon['price'] > $price)
                        {
                            $coupon['price'] = $price;
                        }
                        $coupon_discount_price = (string)($coupon['price'] * 100);
                        $coupon_discount_price = (int)$coupon_discount_price / 100;
                        break;
                    case $coupon['type'] == 5://免费券
                        $coupon_discount_price = (string)($price * 100);
                        $coupon_discount_price = (int)$coupon_discount_price / 100;
                        break;
                    default :
                        $coupon_discount_price = 0;
                        break;
                }
                $coupon_data = [
                    'cid' => $cid,
                    'name' => '优惠券',
                    'title' => $coupon['desc'],
                    'discount_price' => $coupon_discount_price,
                ];
                //算出优惠券活动后需要支付的金额
                $price -= $coupon_discount_price;
            }
        } elseif (!$coupons && $cid)
        {
            throw new ApiException('当前优惠券不可用', 200, 0);
        }
        //当前不是免费下单且支付金额小于0，则抛出异常
        if (!$isFree && $price <= 0)
        {
            //发送钉钉通知
            $active_discount_price = $active_discount_price ?? 0;
            $coupon_discount_price = $coupon_discount_price ?? 0;
            $gids = implode(',', $gids);
            $cid = $cid ?: 0;
            $url = 'https://oapi.dingtalk.com/robot/send?access_token=55abde5b7bf323c0639dcb59f08af08498098dab7b2ec69c152a3b8cf552a4fe';// 钉钉推送地址
            $host = Env::get('host.name');
            Util::sendDingMsg($url, $host . '下单金额异常', "环境：$host\r\n\r\n 用户uid：<span>$uid</span>\r\n\r\n 商品原价：<span>$orPrice</span>\r\n\r\n 商品id：<span>$gids</span>\r\n\r\n 活动优惠金额：<span>$active_discount_price</span>\r\n\r\n 优惠券id：<span>$cid</span>\r\n\r\n 优惠券优惠金额：<span>$coupon_discount_price</span>\r\n\r\n");
            throw new ApiException('服务异常，请稍后重试');
        }
        return [
            'coupon' => $coupon_data ?? [],
            'active' => $active_data ?? [],
            'price' => $price,
        ];
    }


    ############################################## todo 版本2.7.0开始的新版下单逻辑 ################################################

    /**
     * 下单参数处理 v12,v13
     * @param $header
     * @param array $param
     * @param bool $isFree
     * @return mixed
     * @throws ApiException
     * @throws \RedisException
     */
    public static function pay_order_param_format2($header, array &$param, bool $isFree = false)
    {
        $tmpParam = json_encode($param, JSON_UNESCAPED_UNICODE);
        if (Util::commentCheck($header->user['id'], [$tmpParam], 2))
        {
            throw new ApiException('操作频繁，请稍后再试', 200, 0);
        }
        if ($param['gids'])
        {
            $param['gids'] = explode(',', $param['gids']);
            $param['gid'] = (int)current($param['gids']);
        } else
        {
            $param['gids'] = [];
            $param['gid'] = 0;
        }
        $param['order_type'] = (int)$param['order_type'];
        $param['trade_type'] = (int)$param['trade_type'];
        $param['openid'] = $param['openid'] ?? '';//h5支付请求参数
        $param_info = $param['param'] ? json_decode($param['param'], true) : '';
        if (!$param_info && !in_array($param['order_type'], [24, 30, 31, 32, 34], false))
        {
            throw new ApiException('缺少信息，请重试');
        }
        $param['astr_id'] = $param_info['astr_id'] ?? 0;
        $param['cid'] = $param_info['cid'] ?? 0;
        $param['rid'] = $param_info['rid'] ?? 0;
        $param['rid2'] = $param_info['rid2'] ?? 0;
        $param['reid'] = $param_info['reid'] ?? 0;
        $param['start_time'] = $param_info['start_time'] ?? '';//日月年购买时间
        $param['problem'] = $param_info['problem'] ?? '';//定制报告自定义问题
        $param['history_id'] = $param_info['history_id'] ?? '';//塔罗记录id
        $param['live_id'] = $param_info['live_id'] ?? 0;//直播间ID
        $param['live_record_id'] = $param_info['live_record_id'] ?? 0;//直播间记录ID
        $param['in_sign'] = $param_info['in_sign'] ?? 0;//星座ID[冥想]
        $param['data_id'] = $param_info['data_id'] ?? 0;//课程ID[冥想]
        (new LiveRecordingInfo())->checkRecordId($param['live_id'], $param['live_record_id']);
        //验证下单参数
        self::check_pay_unlock_param($header->user['id'], $param);
        //获取吉时吉日记录
        if (Goods::isBaziJsjr($param['order_type'], $param['gid']))
        {
            $GoodTimeSimData = (new Zl4HoroscopeGoodTimeTemp())->getInfoById($param['history_id'], ['uid', 'rid1', 'rid2', 'tid']);
            $tagInfo = (new Zl4GoodTimeTag())->getInfoById($GoodTimeSimData['tid'], ['gid']);
            $param['rid'] = $GoodTimeSimData['rid1'];
            $param['rid2'] = $GoodTimeSimData['rid2'];
            $param['gid'] = $tagInfo['gid'];
            $param['gids'] = [$tagInfo['gid']];
        }
        //塔罗记录获取
        if ($param['order_type'] === 16)
        {
            $tarot = (new ZlTarotRecords())->getInfoById($param['history_id']);
            if (!$tarot || $tarot['uid'] != $header->user['id'] || $tarot['gid'] != $param['gid'])
            {
                throw new ApiException('商品不存在');
            }
            $param['tarot_question'] = $tarot['question'];
            if ($tarot['order_num'])
            {
                $ordersInfo = (new Orders())->OrderRidsInfo($tarot['order_num']);
                if (!in_array((int)$ordersInfo['status'], [0, 2, 3], true))
                {
                    throw new ApiException('塔罗记录状态异常，稍后重试～');
                }
                if ((int)$ordersInfo['status'] === 0)
                {
                    $param['order_num'] = $ordersInfo['order_id'];
                    $param['oid'] = $ordersInfo['id'];
                }
                // throw new ApiException('塔罗记录已生成订单，请前往订单进行查看');
            }
        }

        //运势下单检验下单时间格式，并返回时间戳类型的时间
        if (in_array($param['order_type'], [2, 3, 4, 12, 17], true) || $param['gid'] === 91)
        {
            $param['start_time'] = Util::FormatFortuneTime($param['order_type'], $param['start_time'], $param['gid'])['start_time'];
        }

        //判断起名 | 评分历史是否存在
        if ($param['order_type'] === 35)
        {
            //评分
            if ($param['gid'] === 164)
            {
                $history = (new Zl9NameAnalyzeRecord())->getInfoById($param['history_id']);
            }//起名
            else
            {
                $history = (new Zl9NameAnalyzeCreateNameRecord())->getInfoById($param['history_id']);
            }
            if (!$history || $history['uid'] != $header->user['id'])
            {
                throw new ApiException('记录不存在');
            }
            //个人起名
            if ($param['gid'] === 165 && $history['type'] !== 1)
            {
                throw new ApiException('个人起名记录不存在');
            }//公司起名
            else if ($param['gid'] === 166 && $history['type'] !== 2)
            {
                throw new ApiException('公司起名记录不存在');
            }
        }

        //判断商品是否已经购买
        $res = self::CheckOrder($header->user['id'], $param['gids'], $param['order_type'], $param['rid'], $param['start_time'], $param['reid'], $param['rid2'], $param['data_id'], $param['in_sign']);
        if ($res['msg'])
        {
            throw new ApiException($res['msg']);
        }

        //todo 暂时关闭2019年运购买
//        \App\Models\CacheModel\OrdersLib::CloseOrderFortuneTime($param['order_type'], $param['start_time']);

        //获取购买的商品信息,优惠券信息，活动信息，和参与优惠后的最终价格【'goods'， 'price'， 'active'， 'coupon'】
        self::OrderGoodsInfo2($header, $param, $isFree);
        //获取订单类型和描述语和订单编号
        $order_type_des = self::createOrderId($param['order_type'], $param['gid']);
        $param['pay_des'] = $order_type_des['pay_des'];
        $param['out_trade_no'] = $order_type_des['order_id'];

        //准币支付时检测准币是否充足并添加明细记录
        if ($param['trade_type'] === 4)
        {
            //获取当前商品所需准币
            $param['coin_num'] = self::getOrderCoinNum($param['price']);
            //2.5.0和2.5.3版本之间验证准币密码
            $userCoinInfo = (new ZhunleUserAccount())->getUserCoinFromCache($header->user['id']);
            $user_coin = $userCoinInfo ? (int)$userCoinInfo['balance'] : 0;
            if (Util::CheckAppVersion('2.5.0') !== '小' && Util::CheckAppVersion('2.5.3') === '小')
            {   // 2.5.0版本~2.5.3  进行支付密码验证
                (new ZhunleUserAccount())->verifyCoinPay($userCoinInfo, $param['password'], $param['coin_num']);
            } else
            {
                //检测余额是否足够
                if ($user_coin < $param['coin_num'])
                {
                    throw new ApiException('准币不足，请前去充值', 200, 410);
                }
            }
            //生成明细
            $insertData = [
                'status' => 0,
                'account' => 0,
                'uid' => $header->user['id'],
                'consumer_type' => 2,
                'order_type' => $param['order_type'],
                'gid' => $param['gid'],
                'pray_date' => 0,
                'total_amount' => $param['price'],
                'coin_num' => $param['coin_num'],
                'balance' => $user_coin - $param['coin_num'],
                'order_id' => $param['out_trade_no'],
                'trade_type' => $param['trade_type'],
                'create_time' => time(),
            ];
            //增加准币明细记录
            (new ZhunleUserAccountRecord())->SaveData($header->user['id'], $insertData);
        }
        //下单使用的商户配置id
        $accountInfo = self::getOrderAccountType($param['out_trade_no'], $param['trade_type']);
        $param['account'] = $accountInfo['account'];
        return $param;
    }

    /**
     * 获取下单商品信息
     * @param $param
     * @return array
     * @throws ApiException
     */
    public static function OrderGoodsInfo2($header, &$param, bool $isFree)
    {
        if ($param['gid'] === 91)
        {
            $startTime = !empty($param['start_time']) ? date('Y', $param['start_time']) : date('Y');
        } else
        {
            $startTime = !empty($param['start_time']) ? date('Y-m-d', $param['start_time']) : date('Y-m-d');
        }
        $GoodsModel = (new GoodsCacheModel());
        //存入app信息
        $param['extend']['app_info'] = [
            'version' => $header->headers['version'],
        ];
        //定制报告使用旧逻辑
        if ($param['order_type'] === 15)//获取定制报告的商品信息
        {
            $customGoodsInfo = self::OrderGoodsInfo($header->user['id'], $param, $isFree);
            $param['price'] = $customGoodsInfo['price'];
            $customGoodsInfoItem = current($customGoodsInfo['goods']);
            $key = array_keys($customGoodsInfo['goods']);
            if ($customGoodsInfo['active'])
            {
                $activeData = [
                    'type' => 1,
                    'discount_type' => 1,
                    'attr' => $customGoodsInfo['active']['attr'],
                    'ac_id' => $customGoodsInfo['active']['ac_id'],
                    'name' => $customGoodsInfo['active']['name'],
                    'gname' => $customGoodsInfoItem['question_name'],
                    'des' => $customGoodsInfo['active']['title'],
                    'price' => $customGoodsInfo['price'],
                    'discount_price' => $customGoodsInfo['active']['discount_price'],
                    'num' => 1,
                ];
                $active[current($key)] = $activeData;
            }
            $param['extend']['new_active'] = $active ?? [];
            $param['extend'] = json_encode($param['extend'], JSON_UNESCAPED_UNICODE);
            return $customGoodsInfo;
        }
        if (Goods::isBaziJsjr($param['order_type'], $param['gid']))
        {
            $goods = $GoodsModel->getBaziJsjrGoods($header->user['id'], $param['history_id'], 1)['goods'];
            $goods = array_column($goods, null, 'id');
        } elseif ($param['order_type'] === 5)
        {
            $goods = $GoodsModel->GoodsTypeInfo(5);
            $goods = array_column($goods, null, 'id');
        } elseif ($param['order_type'] === 29)
        {
            $meditationGoods = (new Zl6Meditation())->goodsInfo($param['data_id'], $param['in_sign']);
            $param['extend']['meditation'][$param['data_id']]['in_sign'] = $param['in_sign'];
            $param['extend']['meditation'][$param['data_id']]['title'] = $meditationGoods['name'];
            $goods[$meditationGoods['id']] = $meditationGoods;
            $param['gids'] = [$meditationGoods['id']];
        } elseif ($param['order_type'] === 17)
        {
            $goods = $GoodsModel->getSynastryReportGoods($param['gid']);
        } else
        {
            $goods = $GoodsModel->batchGetInfoByIds($param['gids']);
        }

        //todo 验证当前gid是否是order_type分类下的
//        $orderTypeTmp = array_column($goods, 'type');
//        if (!in_array($param['order_type'], $orderTypeTmp))
//        {
//            throw new ApiException('请求异常,未知的商品', 200, 0);
//        }

        (new ZhunleActivity())->goodsActiveInfo($param['order_type'], $goods, $startTime);
        if ($param['live_id'])
        {
            (new LiveGoodsActivity())->getLiveOrderTypeGoods($param['live_id'], $param['order_type'], $goods);
        }
        $goods_info = $GoodsModel->resetGoodsInfo2($header, $param['order_type'], $goods, $param['rid'], $param['rid2'], $startTime, $param['gids'], ['isGoodInfo' => true, 'start_time' => $startTime, 'rid' => $param['rid']]);
        //组合不同商品的订单拓展信息
        if (Goods::isBaziJsjr($param['order_type'], $param['gid']))
        {
            $param['extend']['history'] = [
                'id' => $param['history_id'],
            ];
        } else
        {
            switch ($param['order_type'])
            {
                case 16:
                    //占卜历史记录信息
                    $history = [
                        'id' => $param['history_id'],
                        'content' => $param['tarot_question'] ?? '',
                    ];
                    $param['extend']['history'] = $history;
                    break;
                case 17:
                    $synastryReportConfig = current(config('synastryreport'))['good_list'][$param['gid']];
                    //合盘记录信息
                    $synastry_report = [
                        'rid1' => $param['rid'],
                        'rid2' => $param['rid2'],
                        'relation_id' => $synastryReportConfig['relation_id'],
                        'srid' => 1,
                        'start_time' => $param['start_time'],
                        'expire_time' => current($goods_info['goods_data'])['expire_time'],
                    ];
                    $param['extend']['synastry_report'] = $synastry_report;
                    break;
                case 24://你的理想型
                    $case = (new Zl1RelationAnalysisIdeaSubmitCase())->getCaseInfo((int)$header->user['id']);
                    if (!$case)
                    {
                        throw new ApiException('服务异常，请稍后再试');
                    }
                    $param['extend']['relation_idea_info'] = [
                        'rids' => $case['rids'],
                        'tag_info' => $case['tag_info'],
                    ];
                    break;
                case 25://怎么追求TA
                    if ($param['rid'] == $param['rid2'])
                    {
                        throw new ApiException('不能选择两个相同的档案');
                    }
                    $param['extend']['recode_ids'] = [
                        'rid1' => $param['rid'],
                        'rid2' => $param['rid2'],
                    ];
                    break;
                case 27:
                    $recodeModel = new Recode();
                    $recode = $recodeModel->getInfoById($param['rid']);
                    $param['extend']['recode_info'] = $recode;
                    if ($param['gid'] === 91)
                    {
                        //获取人生阶段
                        $stateYear = Util::getRecodeStageyear($recode, $startTime);
                        $param['extend']['year_life'] = ['start_time' => $startTime, 'des' => "人生第{$stateYear['num_han']}阶段 ({$stateYear['start_year']}-{$stateYear['end_year']})"];
                    }
                    break;
                case 30:
                case 31:
                    $num = config('ai_answer.goods')[$param['gid']]['num'];
                    $param['extend']['ai_answer'] = ['num' => $num];
                    break;
                case 34:
                    $param['extend']['ai_answer'] = ['duration' => $goods_info['goods_data'][$param['gid']]['expire_time']];
                    break;
                case 35:
                    $param['extend']['history'] = ['id' => $param['history_id']];
                    break;
            }
        }

        $goodTmp = [];
        $gname = '';
        //处理订单活动信息
        foreach ($goods_info['goods_data'] as $good)
        {
            //苹果支付展示苹果商品信息
            if ($param['trade_type'] === 9)
            {
                $good['name'] = $good['apple_goods']['name'] ?? $good['name'];
                $good['price'] = $good['apple_goods']['price'] ?? $good['price'];
            }
            $goodTmp[$good['gid']] = [
                'type' => $param['order_type'],
                'name' => $good['name'],
                'price' => $good['price'],
                'num' => 1,
            ];
            if (!empty($good['active_data']))
            {
                $activeData = [
                    'type' => $good['active_data']['type'],
                    'discount_type' => $good['active_data']['discount_type'],
                    'attr' => $good['active_data']['attr'],
                    'ac_id' => $good['active_data']['id'],
                    'name' => $good['active_data']['name'],
                    'gname' => $good['name'],
                    'des' => $good['active_data']['des'],
                    'price' => $good['active_data']['now_price'],
                    'discount_price' => $good['active_data']['discount_price'],
                    'num' => 1,
                ];
                $active[$good['gid']] = $activeData;
            }
            $gname .= $good['name'] . ',';
        }
        //商品信息
        $param['extend']['goods_info'] = $goodTmp;
        //组成订单extend
        if (isset($active))
        {
            $param['extend']['new_active'] = $active;
        }
        $param['price'] = $goods_info['rec_price'];
        //处理订单优惠券信息
        if (!empty($goods_info['coupon_data']['data']))
        {
            $coupons = $goods_info['coupon_data']['data'];
            $coupons_cid = $coupons ? array_column($coupons, 'id') : 0;
            if ($coupons_cid && $param['cid'])
            {
                if (in_array($param['cid'], $coupons_cid, false))
                {
                    //获取当前优惠券信息
                    $coupon = (new UserCoupon())->getInfoById($param['cid']);
                    //1折扣  2立减劵 3满减 4随机卷,获取算出折扣抵消价格
                    switch (true)
                    {
                        case $coupon['type'] == 1: //若是折扣，直接扣除,
                            $coupon_discount_price = (string)($param['price'] * (1 - $coupon['discount']) * 100);
                            $coupon_discount_price = (int)$coupon_discount_price / 100;
                            break;
                        case in_array($coupon['type'], [2, 3])://若是满减立减，则减去活动价格
                            //若折扣金额大于商品金额，则抵消商品对等金额
                            if ($coupon['discount'] > $param['price'])
                            {
                                $coupon['discount'] = $param['price'];
                            }
                            $coupon_discount_price = (string)($coupon['discount'] * 100);
                            $coupon_discount_price = (int)$coupon_discount_price / 100;
                            break;
                        case $coupon['type'] == 4://随机券
                            //若折扣金额大于商品金额，则抵消商品对等金额
                            if ($coupon['price'] > $param['price'])
                            {
                                $coupon['price'] = $param['price'];
                            }
                            $coupon_discount_price = (string)($coupon['price'] * 100);
                            $coupon_discount_price = (int)$coupon_discount_price / 100;
                            break;
                        case $coupon['type'] == 5://免费券
                            $coupon_discount_price = (string)($param['price'] * 100);
                            $coupon_discount_price = (int)$coupon_discount_price / 100;
                            break;
                        default :
                            $coupon_discount_price = 0;
                            break;
                    }
                    $coupon_data = [
                        'cid' => $param['cid'],
                        'name' => '优惠券',
                        'title' => $coupon['desc'],
                        'discount_price' => $coupon_discount_price,
                    ];
                    //算出优惠券活动后需要支付的金额
                    $param['price'] -= $coupon_discount_price;

                    if (!$param['live_id'] && isset($coupon['live_id']) && $coupon['live_id'])
                    {  // 如果当前下单没有直播间信息 但使用的优惠券是直播间优惠券
                        // 1、获取当前最新直播记录ID
                        $liveRecordId = (new LiveRecordingInfo())->getNewestRecord($param['live_id']);
                        // 2、判断当前直播状态是否在直播中 获取 关播后的指定时间内
                        // 2.1 获取直播间信息
                        $liveRecordInfo = (new LiveRecordingInfo())->getInfoById($liveRecordId, ['status', 'close_time']);
                        // 2.2 获取关播后的指定时间配置信息
                        $configData = (new AppConfig())->configInfo('live');
                        $timeRange = (int)($configData['live_close_earnings_time_range'] ?? 600);
                        if ((int)$liveRecordInfo['status'] === 1 ||
                            ((int)$liveRecordInfo['status'] === 2 && $liveRecordInfo['close_time'] + $timeRange > time()))
                        {
                            $param['live_id'] = $coupon['live_id'];
                        }
                    }
                } else
                {
                    throw new ApiException('当前优惠券不可用', 200, 0);
                }
            }
        }
        //优惠券信息
        if (!empty($coupon_data))
        {
            $param['extend']['coupon'] = $coupon_data;
        }

        $selfRecode = (new \App\Models\Api\Recode())->getSelfRecode(['uid' => $header->user['id']]);
        $selfId = $selfRecode['id'] ?? 0;
        $ridTmp = $param['rid'] == $selfId ? $param['rid'] : $param['rid2'];
        //获取当前用户的内部VIP登等级
        $vipLevel = Util::getVips($header->user['id'], $param['order_type'], $ridTmp);
        //获取vip购买商品的价格
        if ($vipLevel)
        {
            $vipPrice = self::innerVipGoodPrice($header->user['id'], $vipLevel, $param);
            if ($vipPrice !== false)
            {
                $param['price'] = $vipPrice;
            }
            $param['extend']['new_active'] = [];
            $param['extend']['coupon'] = [];
        } //苹果支付价格处理
        elseif ($param['trade_type'] === 9)
        {
            $param['extend']['new_active'] = [];
            $param['extend']['coupon'] = [];
            $param['price'] = current($goodTmp)['price'];
        }
        //获取直播间信息
        if ($param['live_id'])
        {
            $liveInfo = (new LiveAnchorInfo())->getInfoById($param['live_id'], ['live_id', 'name', 'avatar']);
            $liveInfo['live_record_id'] = $liveRecordId ?? (new LiveRecordingInfo())->getNewestRecord($param['live_id']);
            $param['env_type'] = 8;//直播间渠道
            $liveInfo['total_amount'] = $param['price'];
            $liveInfo['gname'] = trim($gname, ',');
            $param['extend']['live_info'] = $liveInfo;
        }
        $param['extend'] = $param['extend'] ? json_encode($param['extend'], JSON_UNESCAPED_UNICODE) : '';
    }


    ############################################## todo 订单通用方法 ################################################

    /**
     * 获取下单描述
     * @param int $order_type
     * @return array
     * @throws ApiException
     * @throws Exception
     */
    public static function createOrderId($order_type = 0, $gid = 0)
    {
        switch ($order_type)
        {
            case  1:
                $pay_des = '准了 - 校准生辰';
                $type = 1;
                break;
            case 2:
            case 3:
            case 4:
            case 12:
                $pay_des = '准了 - 运势';
                $type = 2;
                break;
            case 5:
                $pay_des = '准了 - 幸运之城';
                $type = 3;
                break;
            case 6:
                $pay_des = '准了 - 教学视频';
                $type = 4;
                break;
            case 7:
            case 13:
                $pay_des = '准了 - 一生报告';
                $type = 5;
                break;
            case  8:
                $pay_des = '准了 - 会员';
                $type = 6;
                break;
            case  9:
                $pay_des = '准了 - 准币充值';
                $type = 7;
                break;
            case  10:
                $pay_des = '准了 - 许愿充值';
                $type = 7;
                break;
            case 14:
                $pay_des = '准了 - 商城';//取数据库商品品格名称
                $type = 8;
                break;
            case 15:
                $pay_des = '准了 - 定制报告';
                $type = 9;
                break;
            case 16:
                $pay_des = '准了 - 塔罗';
                $type = 10;
                break;
            case 17:
                $pay_des = '准了 - 合盘报告';
                $type = 11;
                break;
            case 19:
                $pay_des = '准了 - 看盘解答';
                $type = 12;
                break;
            case 20:
                $pay_des = '准了 - 骰子解答';
                $type = 13;
                break;
            case 21:
                $pay_des = '准了 - 塔罗解答';
                $type = 14;
                break;
            case 22:
                $pay_des = '准了 - 问答';
                $type = 15;
                break;
            case 23:
                $pay_des = '准了 - 适合你的TA';
                $type = 16;
                break;
            case 24:
                $pay_des = '准了 - 你的理想型';
                $type = 17;
                break;
            case 25:
                $pay_des = '准了 - 怎么追求TA';
                $type = 18;
                break;
            case 26:
                $pay_des = '准了 - 鉴渣神器';
                $type = 19;
                break;
            case 18:
                $pay_des = '准了 - 心理测评';
                $type = 20;
                break;
            case 27:
                $pay_des = '准了 - 年运拓展';
                $type = 21;
                break;
            case 28:
                $pay_des = '准了 - 八字';
                $type = 22;
                break;
            case 29:
                $pay_des = '准了 - 冥想';
                $type = 23;
                break;
            case 30:
                $pay_des = '准了 - AI解答';
                $type = 25;
                break;
            case 31:
                $pay_des = '准了 - AI解答';
                $type = 26;
                break;
            case 32:
                $pay_des = '准了 - 后天星座';
                $type = 29;
                break;
            case 33:
                $pay_des = '准了 - 连麦礼包';
                $type = 30;
                break;
            case 34:
                $pay_des = '准了 - AI疗愈';
                $type = 31;
                break;
            case 35:
                switch ($gid)
                {
                    case 164://名字评分
                        $pay_des = '准了 - 名字评分';
                        break;
                    case 165://个人起名
                        $pay_des = '准了 - 起名/人名';
                        break;
                    case 166://公司起名
                        $pay_des = '准了 - 起名/公司';
                        break;
                    default:
                        $pay_des = '';
                }
                $type = 32;
                break;
            case 100:
                $pay_des = '连麦咨询';//准有好事
                $type = 24;
                break;
            case 101:
                $pay_des = '悬赏提问';//准有好事
                $type = 27;
                break;
            case 110:
                $pay_des = '对话发送星盘';//准有好事
                $type = 28;
                break;
            default:
                throw new ApiException('未知的商品');
        }
        // 生成唯一订单号  011806192149570386 01=>生时校正 02=>运势 03=>占星地图，04=>占星视频，05=>本命预测，06=>vip，07=>准币,08=>商场，09=>定制报告
        $out_trade_no = str_pad($type, 2, '0', STR_PAD_LEFT) . date('YmdHis') . str_pad(random_int(1, 9999), 4, '0', STR_PAD_LEFT);

        return [
            'num' => $type,
            'pay_des' => $pay_des,
            'order_id' => $out_trade_no,
        ];
    }

    /**
     * 获取商城下单描述
     * @param int $sid 规格id
     * @return array
     * @throws Exception
     */
    public static function createMallOrderId($sid = 0)
    {
        $specInfo = current((new MallGoodsAttr())->batGetInfo([$sid], ['name', 'gid']));
        //获取商品信息
        $goodsInfo = current((new MallGoods())->batGetInfo([$specInfo['gid']], ['name']));
        $type = 8;
        $pay_des = '准了 - ' . $goodsInfo['name'];
        // 生成唯一订单号  011806192149570386 01=>生时校正 02=>运势 03=>占星地图，04=>占星视频，05=>本命预测，06=>vip，07=>准币
        $out_trade_no = str_pad($type, 2, '0', STR_PAD_LEFT) . date('YmdHis') . str_pad(random_int(1, 9999), 4, '0', STR_PAD_LEFT);
        return [
            'num' => $type,
            'pay_des' => $pay_des,
            'order_id' => $out_trade_no,
        ];
    }

    /**
     * 获取订单类型和账号类型
     * @param string $order_id
     * @param int $trade_type 1=>支付宝 2=>微信
     * @return array
     */
    public static function getOrderAccountType($order_id = '', $trade_type = 0)
    {
        $num = (int)substr($order_id, 0, 2);
        $wx_config = config('wxpay');
        $ali_config = config('alipay');
        $trade_type = (int)$trade_type;
        $account = 0;
        switch (true)
        {
            case in_array($trade_type, [1, 7], true)://支付宝
                $trade_type = 1;
                break;
            case in_array($trade_type, [2, 6, 8, 15], true)://微信
                $trade_type = 2;
                break;
        }
        //准币充值和其他商品支付账号区分
        if ($trade_type !== 4)
        {
            //准币商品
            if ($num === 7)
            {
                //支付宝
                if ($trade_type === 1)
                {
                    $account = $ali_config['coin_default'];
                } //微信
                elseif ($trade_type === 2)
                {
                    $account = $wx_config['coin_default'];
                }
            }//商城
            elseif ($num === 8)
            {
                //支付宝
                if ($trade_type === 1)
                {
                    $account = $ali_config['mall_default'];
                } //微信
                elseif ($trade_type === 2)
                {
                    $account = $wx_config['mall_default'];//获取商城微信充值默认支付配置
                }
            } elseif (in_array($num, [9, 12, 13, 14, 15], true))//定制报告,解答,问答
            {
                //支付宝
                if ($trade_type === 1)
                {
                    $account = $ali_config['custom_default'];
                } //微信
                elseif ($trade_type === 2)
                {
                    $account = $wx_config['custom_default'];//获取定制报告微信充值默认支付配置
                }
            } elseif (in_array($num, [51, 52, 53, 54, 55, 56], true))
            {   // 私域H5
                if ($trade_type === 1)
                {
                    $account = $ali_config['privatearea_default'];
                } else
                {
                    $account = $wx_config['privatearea_default'];
                }
            } //虚拟商品
            else
            {
                //支付宝
                if ($trade_type === 1)
                {
                    $account = $ali_config['default'];
                }//微信
                elseif ($trade_type === 2)
                {
                    $account = $wx_config['default'];
                }
            }
        }
        return [
            'num' => $num,
            'account' => $account,
        ];
    }

    /**
     * 获取支付参数
     * 未获取到相关配置时，返回空数组
     * @param string $type 支付类型 ali=>支付宝 wx=>微信支付 jd=>京东支付
     * @param int $account 需要强制获取非使用中的配置，目前主要用于进行订单查询 （下单时不需要传参）
     * @return array
     */
    public static function getPayConfig($type = 'ali', $account = 0)
    {
        $type = strtolower($type) . 'pay';

        $config = config($type);

        if (empty($config))
        {
            return [];
        }

        if ($account > 0 && isset($config['account' . $account]))
        {
            $data = $config['account' . $account];
        } else
        {
            $data = $config['account' . $config['default']];
        }

        return $data;
    }

    /**
     * 获取当前订单是否需要计算
     * @param array $unlock_gids 解锁商品
     * @param int $order_type
     * @return int
     */
    public static function getOrderStatus($unlock_gids, $order_type)
    {
        $order_type = (int)$order_type;
        $count_num = array_intersect($unlock_gids, [18, 19, 105]);
        if ($order_type === 5 && count($count_num) >= 1)
        {
            $is_have_fate = true;
        } elseif ($order_type === 22 && (int)current($unlock_gids) !== 83)
        {
            $is_have_fate = true;
        } else
        {
            $is_have_fate = false;
        }
        if (Goods::isBaziJsjr($order_type, current($unlock_gids)))
        {
            $status = 4;
        } elseif (in_array($order_type, [2, 6, 8, 9, 10, 11, 16, 29, 30, 31, 32, 34], true) || (in_array($order_type, [5, 22], true) && $is_have_fate === false))//无需计算的订单
        {
            $status = 4;
        } else
        {
            $status = 1;
        }
        return $status;
    }

    /**
     * 获取vip购买商品需要的价格
     * @param int $uid 用户id
     * @param int $vipLevel 内部vip等级
     * @param array $param [order_type=>1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8=>vip会员 9=>准币（在user_account表中）  start_time=>运势购买的开始时间 （order_type为 2，3，4） gids=>购买的商品id （order_type为 8 ） trade_type=>支付方式
     * @return bool|float
     * @throws Exception
     */
    public static function innerVipGoodPrice($uid, $vipLevel, $param)
    {
        $price = false;//初始化价格
        $order_type = (int)$param['order_type'];
        $trade_type = (int)$param['trade_type'];
        $start_time = $param['start_time'] ?? 0;
        $gids = $param['gids'] ?? [];
        $gid = $gids ? (int)current($gids) : 0;

        //VIP1商品价格全部为0。01
        if ($vipLevel === 1)
        {
            return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
        }
        //VIP2的价格规则
        if ($vipLevel === 2)
        {
            //日运从第二天开始只能购买一个月,商品id=>3
            //新规则：日运从第二天开始只能购买一个月,商品id=>3，只能购买未来一年内  新规则日期：2020-09-29   新规则制定人：郭鲲生
            if ($order_type === 2 && in_array(3, $gids, false))
            {
                $now_day = strtotime(date('Y-m-d'));//当前日期时间戳
                $elevenDay = strtotime('+1year', $now_day);//距离档期日期后11个月的时间戳
                //若购买时间在当前时间和未来十一个月时间内则一分钱购买
                if ($start_time > $now_day && $start_time < $elevenDay)
                {
                    return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                }
            }//月运只有当前月的1分钱并且购买的是一个月的,商品id=>20
            //新规则：月运只有当前月的1分钱并且购买的是一个月的,商品id=>20，只能购买未来一年内    新规则日期：2020-09-29   新规则制定人：郭鲲生
            elseif ($order_type === 3 && in_array(20, $gids, false))
            {
                $now_month = strtotime(date('Y-m-1'));
                $elevenMonth = strtotime('+1year', $now_month);//距离档期日期后11个月的时间戳
                //若购买时间在当前时间和未来十一个月时间内则一分钱购买
                if ($start_time >= $now_month && $start_time < $elevenMonth)
                {
                    return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                }
            } //年运只有当前年的1分钱并且购买的是一年的,商品id=>24
            //新规则：年运只有当前年的1分钱并且购买的是一年的,商品id=>24，只能购买今年和明年  新规则日期：2020-09-29   新规则制定人：郭鲲生
            elseif ($order_type === 4 && in_array(24, $gids, false))
            {
                $now_year = strtotime(date('Y-1-1'));
                $elevenYear = strtotime('+2year', $now_year);//距离档期日期后11个月的时间戳
                //若购买时间在当前时间和未来十一个月时间内则一分钱购买
                if ($start_time >= $now_year && $start_time < $elevenYear)
                {
                    return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                }
            } elseif ($order_type === 8)//不在vip有效期内并且购买的是一个月的会员一分钱,商品id=>35
            {
                $UserVip = self::getUserVipTime($uid, current($gids));//返回时间为时间戳
                if ((!isset($UserVip['is_valid']) || !$UserVip['is_valid']) && in_array(35, $gids, false))
                {
                    return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                }
            } elseif ($order_type === 17 && in_array($gid, [62, 64, 66, 70], false))//合盘双方中有自己的档案，购买【任意关系】【未来一年】开始时间范围（当前年和下一年,完整的两年） 0.01元； 新规则日期：2021-11-15  新规则制定人：郭鲲生
            {
                // 获取当前用户的档案id
                $selfId = (new \App\Models\Api\Recode())->getSelfRecode(['uid' => $uid]);
                if ($selfId['id'] == $param['rid'] || $selfId['id'] == $param['rid2'])
                {
                    $now_day = strtotime(date('Y-1-1'));//当前日期时间戳
                    $lastYear = strtotime('+2year', $now_day);
                    //若购买时间在当前时间和未来十一个月时间内则一分钱购买
                    if ($start_time >= $now_day && $start_time < $lastYear)
                    {
                        return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                    }
                }
            } elseif (in_array($order_type, [27, 28], true))//自己的档案 人生阶段、年运筛查、八字  随便买，0.01元 新规则制定人：郭鲲生
            {
                // 获取当前用户的档案id
                $selfId = (new \App\Models\Api\Recode())->getSelfRecode(['uid' => $uid]);
                if ($selfId['id'] == $param['rid'] || $selfId['id'] == $param['rid2'])
                {
                    return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
                }
            } elseif (in_array($order_type, [5, 7, 30, 31], true))//购买0.01元
            {
                return self::checkInnerVipUnlockOrderTradeType($trade_type, 0.01);
            }
        }
        return $price;
    }

    /**
     * 检测内部用户支付方式
     * @param $trade_type
     * @param $price
     * @return int
     */
    protected static function checkInnerVipUnlockOrderTradeType($trade_type, $price)
    {
        if ($trade_type === 4)
        {
            return 0;
        }
        return $price;
    }

    /**
     * 获取用户准币余额
     * @param int $uid
     * @return int
     * @throws Exception
     */
    public static function getCoinNum($uid)
    {
        $user_coin = (new ZhunleUserAccount())->getUserCoinFromCache($uid);
        return $user_coin ? (int)$user_coin['balance'] : 0;
    }

    /**
     * 获取订单准币汇率
     * @param $price
     * @return false|float|int
     */
    public static function getOrderCoinNum($price)
    {
        return $price ? floor($price * 10) : 0;
    }

    /**
     * 获取订单准币金额
     * @param $coin
     * @return float|int
     */
    public static function getOrderCoinTotalAmount($coin)
    {
        return $coin ? ($coin / 10) : 0;
    }

    /**
     * todo 支付宝未支付订单查询不到无法关闭
     * 平台关闭订单
     * @param $orderId
     * @return bool
     * @throws Exception
     */
    public static function closePlatformOrder($orderId)
    {
        $orderInfo = (new OrdersCacheModel())->getInfoById($orderId, ['trade_type']);
        $accountConfig = self::getOrderAccountType($orderId, $orderInfo['trade_type']);
        if ((int)$orderInfo['trade_type'] === 1)
        {
            $response = \GuzzleHttp\json_decode(\GuzzleHttp\json_encode((new Ali())->close($orderId, (int)$accountConfig['account'])), true);
            if (!empty($response->code) && (int)$response->code === 10000)
            {
                return true;
            }
            Log::write('支付宝平台退款失败:' . json_encode($response, true), 'error', true);
            return false;

        }

        if ((int)$orderInfo['trade_type'] === 2)
        {
            $weixin = new Weixin();
            $wxpayConfig = \app\common\lib\Orders::getPayConfig('wx', $accountConfig['account']);
            $wxPayType = $wxpayConfig['wx_pay_type'][$orderInfo['trade_type']];
            // 获取微信配置信息
            $wxConfig = $wxpayConfig[$wxPayType['configName']];
            $res = $weixin->close($orderId, $wxConfig);
            if ($res['return_code'] === 'SUCCESS')
            {
                return true;
            }
            Log::write('微信平台退款失败:' . json_encode($res, true), 'error', true);
            return false;
        }
    }
}
